1<?php 2/** 3 * Zend Framework 4 * 5 * LICENSE 6 * 7 * This source file is subject to the new BSD license that is bundled 8 * with this package in the file LICENSE.txt. 9 * It is also available through the world-wide-web at this URL: 10 * http://framework.zend.com/license/new-bsd 11 * If you did not receive a copy of the license and are unable to 12 * obtain it through the world-wide-web, please send an email 13 * to license@zend.com so we can send you a copy immediately. 14 * 15 * @category Zend 16 * @package Zend_Log 17 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 18 * @license http://framework.zend.com/license/new-bsd New BSD License 19 * @version $Id$ 20 */ 21 22/** 23 * @category Zend 24 * @package Zend_Log 25 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 26 * @license http://framework.zend.com/license/new-bsd New BSD License 27 * @version $Id$ 28 * 29 * Convenience methods for log [@see Zend_Log::__call()]: 30 * 31 * @method emerg(string $message, $extras = null) 32 * @method alert(string $message, $extras = null) 33 * @method crit(string $message, $extras = null) 34 * @method err(string $message, $extras = null) 35 * @method warn(string $message, $extras = null) 36 * @method notice(string $message, $extras = null) 37 * @method info(string $message, $extras = null) 38 * @method debug(string $message, $extras = null) 39 */ 40class Zend_Log 41{ 42 const EMERG = 0; // Emergency: system is unusable 43 const ALERT = 1; // Alert: action must be taken immediately 44 const CRIT = 2; // Critical: critical conditions 45 const ERR = 3; // Error: error conditions 46 const WARN = 4; // Warning: warning conditions 47 const NOTICE = 5; // Notice: normal but significant condition 48 const INFO = 6; // Informational: informational messages 49 const DEBUG = 7; // Debug: debug messages 50 51 /** 52 * @var array of priorities where the keys are the 53 * priority numbers and the values are the priority names 54 */ 55 protected $_priorities = array(); 56 57 /** 58 * @var array of Zend_Log_Writer_Abstract 59 */ 60 protected $_writers = array(); 61 62 /** 63 * @var array of Zend_Log_Filter_Interface 64 */ 65 protected $_filters = array(); 66 67 /** 68 * @var array of extra log event 69 */ 70 protected $_extras = array(); 71 72 /** 73 * 74 * @var string 75 */ 76 protected $_defaultWriterNamespace = 'Zend_Log_Writer'; 77 78 /** 79 * 80 * @var string 81 */ 82 protected $_defaultFilterNamespace = 'Zend_Log_Filter'; 83 84 /** 85 * 86 * @var string 87 */ 88 protected $_defaultFormatterNamespace = 'Zend_Log_Formatter'; 89 90 /** 91 * 92 * @var callback 93 */ 94 protected $_origErrorHandler = null; 95 96 /** 97 * 98 * @var boolean 99 */ 100 protected $_registeredErrorHandler = false; 101 102 /** 103 * 104 * @var array|boolean 105 */ 106 protected $_errorHandlerMap = false; 107 108 /** 109 * 110 * @var string 111 */ 112 protected $_timestampFormat = 'c'; 113 114 /** 115 * Class constructor. Create a new logger 116 * 117 * @param Zend_Log_Writer_Abstract|null $writer default writer 118 */ 119 public function __construct(Zend_Log_Writer_Abstract $writer = null) 120 { 121 $r = new ReflectionClass($this); 122 $this->_priorities = array_flip($r->getConstants()); 123 124 if ($writer !== null) { 125 $this->addWriter($writer); 126 } 127 } 128 129 /** 130 * Factory to construct the logger and one or more writers 131 * based on the configuration array 132 * 133 * @param array|Zend_Config Array or instance of Zend_Config 134 * @return Zend_Log 135 * @throws Zend_Log_Exception 136 */ 137 static public function factory($config = array()) 138 { 139 if ($config instanceof Zend_Config) { 140 $config = $config->toArray(); 141 } 142 143 if (!is_array($config) || empty($config)) { 144 /** @see Zend_Log_Exception */ 145 throw new Zend_Log_Exception('Configuration must be an array or instance of Zend_Config'); 146 } 147 148 if (array_key_exists('className', $config)) { 149 $class = $config['className']; 150 unset($config['className']); 151 } else { 152 $class = __CLASS__; 153 } 154 155 $log = new $class; 156 157 if (!$log instanceof Zend_Log) { 158 /** @see Zend_Log_Exception */ 159 throw new Zend_Log_Exception('Passed className does not belong to a descendant of Zend_Log'); 160 } 161 162 if (array_key_exists('timestampFormat', $config)) { 163 if (null != $config['timestampFormat'] && '' != $config['timestampFormat']) { 164 $log->setTimestampFormat($config['timestampFormat']); 165 } 166 unset($config['timestampFormat']); 167 } 168 169 if (!is_array(current($config))) { 170 $log->addWriter(current($config)); 171 } else { 172 foreach($config as $writer) { 173 $log->addWriter($writer); 174 } 175 } 176 177 return $log; 178 } 179 180 181 /** 182 * Construct a writer object based on a configuration array 183 * 184 * @param array $config config array with writer spec 185 * @return Zend_Log_Writer_Abstract 186 * @throws Zend_Log_Exception 187 */ 188 protected function _constructWriterFromConfig($config) 189 { 190 $writer = $this->_constructFromConfig('writer', $config, $this->_defaultWriterNamespace); 191 192 if (!$writer instanceof Zend_Log_Writer_Abstract) { 193 $writerName = is_object($writer) 194 ? get_class($writer) 195 : 'The specified writer'; 196 /** @see Zend_Log_Exception */ 197 throw new Zend_Log_Exception("{$writerName} does not extend Zend_Log_Writer_Abstract!"); 198 } 199 200 if (isset($config['filterName'])) { 201 $filter = $this->_constructFilterFromConfig($config); 202 $writer->addFilter($filter); 203 } 204 205 if (isset($config['formatterName'])) { 206 $formatter = $this->_constructFormatterFromConfig($config); 207 $writer->setFormatter($formatter); 208 } 209 210 return $writer; 211 } 212 213 /** 214 * Construct filter object from configuration array or Zend_Config object 215 * 216 * @param array|Zend_Config $config Zend_Config or Array 217 * @return Zend_Log_Filter_Interface 218 * @throws Zend_Log_Exception 219 */ 220 protected function _constructFilterFromConfig($config) 221 { 222 $filter = $this->_constructFromConfig('filter', $config, $this->_defaultFilterNamespace); 223 224 if (!$filter instanceof Zend_Log_Filter_Interface) { 225 $filterName = is_object($filter) 226 ? get_class($filter) 227 : 'The specified filter'; 228 /** @see Zend_Log_Exception */ 229 throw new Zend_Log_Exception("{$filterName} does not implement Zend_Log_Filter_Interface"); 230 } 231 232 return $filter; 233 } 234 235 /** 236 * Construct formatter object from configuration array or Zend_Config object 237 * 238 * @param array|Zend_Config $config Zend_Config or Array 239 * @return Zend_Log_Formatter_Interface 240 * @throws Zend_Log_Exception 241 */ 242 protected function _constructFormatterFromConfig($config) 243 { 244 $formatter = $this->_constructFromConfig('formatter', $config, $this->_defaultFormatterNamespace); 245 246 if (!$formatter instanceof Zend_Log_Formatter_Interface) { 247 $formatterName = is_object($formatter) 248 ? get_class($formatter) 249 : 'The specified formatter'; 250 /** @see Zend_Log_Exception */ 251 throw new Zend_Log_Exception($formatterName . ' does not implement Zend_Log_Formatter_Interface'); 252 } 253 254 return $formatter; 255 } 256 257 /** 258 * Construct a filter or writer from config 259 * 260 * @param string $type 'writer' of 'filter' 261 * @param mixed $config Zend_Config or Array 262 * @param string $namespace 263 * @return object 264 * @throws Zend_Log_Exception 265 */ 266 protected function _constructFromConfig($type, $config, $namespace) 267 { 268 if ($config instanceof Zend_Config) { 269 $config = $config->toArray(); 270 } 271 272 if (!is_array($config) || empty($config)) { 273 throw new Zend_Log_Exception( 274 'Configuration must be an array or instance of Zend_Config' 275 ); 276 } 277 278 $params = isset($config[ $type .'Params' ]) ? $config[ $type .'Params' ] : array(); 279 $className = $this->getClassName($config, $type, $namespace); 280 if (!class_exists($className)) { 281 Zend_Loader::loadClass($className); 282 } 283 284 $reflection = new ReflectionClass($className); 285 if (!$reflection->implementsInterface('Zend_Log_FactoryInterface')) { 286 throw new Zend_Log_Exception( 287 $className . ' does not implement Zend_Log_FactoryInterface and can not be constructed from config.' 288 ); 289 } 290 291 return call_user_func(array($className, 'factory'), $params); 292 } 293 294 /** 295 * Get the writer or filter full classname 296 * 297 * @param array $config 298 * @param string $type filter|writer 299 * @param string $defaultNamespace 300 * @return string full classname 301 * @throws Zend_Log_Exception 302 */ 303 protected function getClassName($config, $type, $defaultNamespace) 304 { 305 if (!isset($config[$type . 'Name'])) { 306 throw new Zend_Log_Exception("Specify {$type}Name in the configuration array"); 307 } 308 309 $className = $config[$type . 'Name']; 310 $namespace = $defaultNamespace; 311 312 if (isset($config[$type . 'Namespace'])) { 313 $namespace = $config[$type . 'Namespace']; 314 } 315 316 // PHP >= 5.3.0 namespace given? 317 if (substr($namespace, -1) == '\\') { 318 return $namespace . $className; 319 } 320 321 // empty namespace given? 322 if (strlen($namespace) === 0) { 323 return $className; 324 } 325 326 return $namespace . '_' . $className; 327 } 328 329 /** 330 * Packs message and priority into Event array 331 * 332 * @param string $message Message to log 333 * @param integer $priority Priority of message 334 * @return array Event array 335 */ 336 protected function _packEvent($message, $priority) 337 { 338 return array_merge(array( 339 'timestamp' => date($this->_timestampFormat), 340 'message' => $message, 341 'priority' => $priority, 342 'priorityName' => $this->_priorities[$priority] 343 ), 344 $this->_extras 345 ); 346 } 347 348 /** 349 * Class destructor. Shutdown log writers 350 * 351 * @return void 352 */ 353 public function __destruct() 354 { 355 /** @var Zend_Log_Writer_Abstract $writer */ 356 foreach($this->_writers as $writer) { 357 $writer->shutdown(); 358 } 359 } 360 361 /** 362 * Undefined method handler allows a shortcut: 363 * $log->priorityName('message') 364 * instead of 365 * $log->log('message', Zend_Log::PRIORITY_NAME) 366 * 367 * @param string $method priority name 368 * @param string $params message to log 369 * @return void 370 * @throws Zend_Log_Exception 371 */ 372 public function __call($method, $params) 373 { 374 $priority = strtoupper($method); 375 if (($priority = array_search($priority, $this->_priorities)) !== false) { 376 switch (count($params)) { 377 case 0: 378 /** @see Zend_Log_Exception */ 379 throw new Zend_Log_Exception('Missing log message'); 380 case 1: 381 $message = array_shift($params); 382 $extras = null; 383 break; 384 default: 385 $message = array_shift($params); 386 $extras = array_shift($params); 387 break; 388 } 389 $this->log($message, $priority, $extras); 390 } else { 391 /** @see Zend_Log_Exception */ 392 throw new Zend_Log_Exception('Bad log priority'); 393 } 394 } 395 396 /** 397 * Log a message at a priority 398 * 399 * @param string $message Message to log 400 * @param integer $priority Priority of message 401 * @param mixed $extras Extra information to log in event 402 * @return void 403 * @throws Zend_Log_Exception 404 */ 405 public function log($message, $priority, $extras = null) 406 { 407 // sanity checks 408 if (empty($this->_writers)) { 409 /** @see Zend_Log_Exception */ 410 throw new Zend_Log_Exception('No writers were added'); 411 } 412 413 if (! isset($this->_priorities[$priority])) { 414 /** @see Zend_Log_Exception */ 415 throw new Zend_Log_Exception('Bad log priority'); 416 } 417 418 // pack into event required by filters and writers 419 $event = $this->_packEvent($message, $priority); 420 421 // Check to see if any extra information was passed 422 if (!empty($extras)) { 423 $info = array(); 424 if (is_array($extras)) { 425 foreach ($extras as $key => $value) { 426 if (is_string($key)) { 427 $event[$key] = $value; 428 } else { 429 $info[] = $value; 430 } 431 } 432 } else { 433 $info = $extras; 434 } 435 if (!empty($info)) { 436 $event['info'] = $info; 437 } 438 } 439 440 // abort if rejected by the global filters 441 /** @var Zend_Log_Filter_Interface $filter */ 442 foreach ($this->_filters as $filter) { 443 if (! $filter->accept($event)) { 444 return; 445 } 446 } 447 448 // send to each writer 449 /** @var Zend_Log_Writer_Abstract $writer */ 450 foreach ($this->_writers as $writer) { 451 $writer->write($event); 452 } 453 } 454 455 /** 456 * Add a custom priority 457 * 458 * @param string $name Name of priority 459 * @param integer $priority Numeric priority 460 * @return $this 461 * @throws Zend_Log_Exception 462 */ 463 public function addPriority($name, $priority) 464 { 465 // Priority names must be uppercase for predictability. 466 $name = strtoupper($name); 467 468 if (isset($this->_priorities[$priority]) 469 || false !== array_search($name, $this->_priorities)) { 470 /** @see Zend_Log_Exception */ 471 throw new Zend_Log_Exception('Existing priorities cannot be overwritten'); 472 } 473 474 $this->_priorities[$priority] = $name; 475 return $this; 476 } 477 478 /** 479 * Add a filter that will be applied before all log writers. 480 * Before a message will be received by any of the writers, it 481 * must be accepted by all filters added with this method. 482 * 483 * @param int|Zend_Config|array|Zend_Log_Filter_Interface $filter 484 * @return $this 485 * @throws Zend_Log_Exception 486 */ 487 public function addFilter($filter) 488 { 489 if (is_int($filter)) { 490 /** @see Zend_Log_Filter_Priority */ 491 $filter = new Zend_Log_Filter_Priority($filter); 492 493 } elseif ($filter instanceof Zend_Config || is_array($filter)) { 494 $filter = $this->_constructFilterFromConfig($filter); 495 496 } elseif(! $filter instanceof Zend_Log_Filter_Interface) { 497 /** @see Zend_Log_Exception */ 498 throw new Zend_Log_Exception('Invalid filter provided'); 499 } 500 501 $this->_filters[] = $filter; 502 return $this; 503 } 504 505 /** 506 * Add a writer. A writer is responsible for taking a log 507 * message and writing it out to storage. 508 * 509 * @param mixed $writer Zend_Log_Writer_Abstract or Config array 510 * @return Zend_Log 511 * @throws Zend_Log_Exception 512 */ 513 public function addWriter($writer) 514 { 515 if (is_array($writer) || $writer instanceof Zend_Config) { 516 $writer = $this->_constructWriterFromConfig($writer); 517 } 518 519 if (!$writer instanceof Zend_Log_Writer_Abstract) { 520 /** @see Zend_Log_Exception */ 521 throw new Zend_Log_Exception( 522 'Writer must be an instance of Zend_Log_Writer_Abstract' 523 . ' or you should pass a configuration array' 524 ); 525 } 526 527 $this->_writers[] = $writer; 528 return $this; 529 } 530 531 /** 532 * Set an extra item to pass to the log writers. 533 * 534 * @param string $name Name of the field 535 * @param string $value Value of the field 536 * @return Zend_Log 537 */ 538 public function setEventItem($name, $value) 539 { 540 $this->_extras = array_merge($this->_extras, array($name => $value)); 541 return $this; 542 } 543 544 /** 545 * Register Logging system as an error handler to log php errors 546 * Note: it still calls the original error handler if set_error_handler is able to return it. 547 * 548 * Errors will be mapped as: 549 * E_NOTICE, E_USER_NOTICE => NOTICE 550 * E_WARNING, E_CORE_WARNING, E_USER_WARNING => WARN 551 * E_ERROR, E_USER_ERROR, E_CORE_ERROR, E_RECOVERABLE_ERROR => ERR 552 * E_DEPRECATED, E_STRICT, E_USER_DEPRECATED => DEBUG 553 * (unknown/other) => INFO 554 * 555 * @link http://www.php.net/manual/en/function.set-error-handler.php Custom error handler 556 * 557 * @return Zend_Log 558 */ 559 public function registerErrorHandler() 560 { 561 // Only register once. Avoids loop issues if it gets registered twice. 562 if ($this->_registeredErrorHandler) { 563 return $this; 564 } 565 566 $this->_origErrorHandler = set_error_handler(array($this, 'errorHandler')); 567 568 // Contruct a default map of phpErrors to Zend_Log priorities. 569 // Some of the errors are uncatchable, but are included for completeness 570 $this->_errorHandlerMap = array( 571 E_NOTICE => Zend_Log::NOTICE, 572 E_USER_NOTICE => Zend_Log::NOTICE, 573 E_WARNING => Zend_Log::WARN, 574 E_CORE_WARNING => Zend_Log::WARN, 575 E_USER_WARNING => Zend_Log::WARN, 576 E_ERROR => Zend_Log::ERR, 577 E_USER_ERROR => Zend_Log::ERR, 578 E_CORE_ERROR => Zend_Log::ERR, 579 E_RECOVERABLE_ERROR => Zend_Log::ERR, 580 E_STRICT => Zend_Log::DEBUG, 581 ); 582 // PHP 5.3.0+ 583 if (defined('E_DEPRECATED')) { 584 $this->_errorHandlerMap['E_DEPRECATED'] = Zend_Log::DEBUG; 585 } 586 if (defined('E_USER_DEPRECATED')) { 587 $this->_errorHandlerMap['E_USER_DEPRECATED'] = Zend_Log::DEBUG; 588 } 589 590 $this->_registeredErrorHandler = true; 591 return $this; 592 } 593 594 /** 595 * Error Handler will convert error into log message, and then call the original error handler 596 * 597 * @link http://www.php.net/manual/en/function.set-error-handler.php Custom error handler 598 * @param int $errno 599 * @param string $errstr 600 * @param string $errfile 601 * @param int $errline 602 * @param array $errcontext 603 * @return boolean 604 */ 605 public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) 606 { 607 $errorLevel = error_reporting(); 608 609 if ($errorLevel & $errno) { 610 if (isset($this->_errorHandlerMap[$errno])) { 611 $priority = $this->_errorHandlerMap[$errno]; 612 } else { 613 $priority = Zend_Log::INFO; 614 } 615 $this->log($errstr, $priority, array('errno'=>$errno, 'file'=>$errfile, 'line'=>$errline, 'context'=>$errcontext)); 616 } 617 618 if ($this->_origErrorHandler !== null) { 619 return call_user_func($this->_origErrorHandler, $errno, $errstr, $errfile, $errline, $errcontext); 620 } 621 return false; 622 } 623 624 /** 625 * Set timestamp format for log entries. 626 * 627 * @param string $format 628 * @return Zend_Log 629 */ 630 public function setTimestampFormat($format) 631 { 632 $this->_timestampFormat = $format; 633 return $this; 634 } 635 636 /** 637 * Get timestamp format used for log entries. 638 * 639 * @return string 640 */ 641 public function getTimestampFormat() 642 { 643 return $this->_timestampFormat; 644 } 645} 646