PageRenderTime 49ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/EventLog/src/log.php

https://github.com/Yannix/zetacomponents
PHP | 624 lines | 141 code | 35 blank | 448 comment | 6 complexity | b3f4fb5c90bf22c0fdc317d39a42116d MD5 | raw file
  1. <?php
  2. /**
  3. * File containing the ezcLog class.
  4. *
  5. * Licensed to the Apache Software Foundation (ASF) under one
  6. * or more contributor license agreements. See the NOTICE file
  7. * distributed with this work for additional information
  8. * regarding copyright ownership. The ASF licenses this file
  9. * to you under the Apache License, Version 2.0 (the
  10. * "License"); you may not use this file except in compliance
  11. * with the License. You may obtain a copy of the License at
  12. *
  13. * http://www.apache.org/licenses/LICENSE-2.0
  14. *
  15. * Unless required by applicable law or agreed to in writing,
  16. * software distributed under the License is distributed on an
  17. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  18. * KIND, either express or implied. See the License for the
  19. * specific language governing permissions and limitations
  20. * under the License.
  21. *
  22. * @package EventLog
  23. * @version //autogentag//
  24. * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
  25. */
  26. /**
  27. * The ezcLog class records log messages and audit trails to one or multiple
  28. * writers.
  29. *
  30. * Available writers are:
  31. * - {@link ezcLogUnixFileWriter Unix File} writer
  32. * - {@link ezcLogDatabaseWriter Database} writer
  33. *
  34. * Extra writers can be added by implementing the {@link ezcLogWriter} interface.
  35. *
  36. * Use the {@link getMapper()} method to get an instance of the ezcLogMapper.
  37. * The ezcLogMapper classes specifies incoming log messages with the {@link ezcLogFilter}.
  38. * Log messages that are accepted, match with the filter, are sent to the
  39. * {@link ezcLogWriter}.
  40. *
  41. * The following example demonstrates how all log messages, except for the
  42. * audit trailing and debug messages, are written to a file.
  43. * <code>
  44. * $filter = new ezcLogFilter();
  45. * $filter->severity = ezcLog::INFO | ezcLog::NOTICE | ezcLog::WARNING | ezcLog::ERROR | ezcLog::FATAL;
  46. *
  47. * $log = ezcLog::getInstance();
  48. * $log->getMapper()->appendRule( new ezcLogFilterRule( $filter, new ezcLogUnixFileWriter( "/tmp/logs/", "error.log" ), true ) );
  49. * </code>
  50. *
  51. * The log messages with the severity: INFO, NOTICE, WARNING, ERROR, and FATAL will
  52. * be written to the file: "/tmp/logs/error.log". See {@link ezcLogUnixFileWriter} for
  53. * the description of the file format.
  54. *
  55. * The following example will write the audit trails to the database:
  56. * <code>
  57. * $filter = new ezcLogFilter();
  58. * $filter->severity = ezcLog::SUCCESS_AUDIT | ezcLog::FAILED_AUDIT;
  59. *
  60. * $log = ezcLog::getInstance();
  61. * $log->getMapper()->appendRule( new ezcLogFilterRule( $filter, new ezcLogDatabaseWriter( "audits" ), true ) );
  62. * </code>
  63. *
  64. * The audit trails will be stored in the table "audits". See {@link ezcLogDatabaseWriter}
  65. * for creating the appropriate tables and setting up the database. See the {@link ezcLogFilter}
  66. * for more details.
  67. *
  68. * Use the {@link log()} method to log messages at the specified writers. This
  69. * method expects a:
  70. * - Message, contains a single log message.
  71. * - Severity, indicates the level of importance.
  72. * - Extra attributes (optional).
  73. *
  74. * Although the interpretation of the severity levels are up to the programmer,
  75. * the most common interpretations are:
  76. * - DEBUG: Records information about the progress in the program and references
  77. * source code functions. Knowledge of the source code is needed to interpret
  78. * this log message.
  79. * - INFO: Informative logging at a detailed level. This logging method produces a
  80. * high level of logging, which is unmanageable on a production environment.
  81. * Usually INFO logging is only enabled to help by analysing a problem.
  82. * - NOTICE: Informative logging at a lower detail level than INFO logging.
  83. * Only major stages are recorded and is useful to monitor a low volume system.
  84. * - WARNING: Something unexpected happened, but did not cause any loss of service.
  85. * - ERROR: An error occured, which may cause partial loss of service. Usually the
  86. * system can recover.
  87. * - FATAL: An serious error occured and the system is unlikely to recover.
  88. * - SUCCESS_AUDIT: Informative logging about a successful completion of work by
  89. * a module completed. Useful to trace system changes directly or indirectly
  90. * done by a user.
  91. * - FAILED_AUDIT: Informative logging about an action from a module
  92. * with a negative result. A failed login will most likely added to this severity.
  93. *
  94. * The next example logs a fatal error and has no extra attributes:
  95. * <code>
  96. * ezcLog::getInstance()->log( "Cannot open ini file: <$file>", ezcLog::FATAL );
  97. * </code>
  98. *
  99. * The log message will get by default the category and source: "default". The
  100. * default values can be modified by changing, respectively, the properties
  101. * $category and $source.
  102. *
  103. * An example of a Payment checker is as follows:
  104. * <code>
  105. * // The start of the Payment module.
  106. * $log = ezcLog::getInstance();
  107. * $log->source = "Payment checker"; // Change the default source.
  108. *
  109. * $log->log( "Checking the received amount", ezcLog::INFO, array( "shop" ) );
  110. *
  111. * if ( !$eZPay->receivedAmount() != $requiredAmount )
  112. * {
  113. * $log->log( "Received amount: <".$eZPay->receivedAmount()."> expected: <$requiredAmount>.",
  114. * ezcLog::DEBUG,
  115. * array( "category" => "shop", "file" => __FILE__, "line" => __LINE )
  116. * );
  117. *
  118. * $log->log( "Insufficient amount.",
  119. * ezcLog::FAILED_AUDIT,
  120. * array( "UserName" => getCurrentUser(), category => "Payment" )
  121. * )
  122. *
  123. * $log->log( "Rollback amount not implemented, cannot recover, ezcLog::FATAL );
  124. * exit();
  125. * }
  126. * </code>
  127. *
  128. * Sometimes information repeats for specific severities or categories. For example that
  129. * for the audit trails an username is required. Convenience methods like:
  130. * {@link setSeverityAttributes()} and {@link setSourceAttributes()} exist to append
  131. * information automatically to the log message.
  132. *
  133. * The ezcLog class provides a {@link trigger_error()} log handler: {@link ezcLog::logHandler()}.
  134. * Using the trigger_error method makes your code less Log package dependent and
  135. * produces less overhead when logging is disabled.
  136. *
  137. * See the {@link ezcLog::logHandler()} method for more information about how to set up the
  138. * trigger_error functionality.
  139. *
  140. * See the {@link ezcDebug} package for more detailed information about writing DEBUG
  141. * messages.
  142. *
  143. * @property string $source
  144. * Definition of the global location where the log message comes
  145. * from. Some examples are: module, source file, extension, etc. The
  146. * source depends also on the severity of the message. For DEBUG
  147. * messages is the source file more important whereas for a FATAL
  148. * error the module is sufficient.
  149. * @property string $category
  150. * Definition of the message group. Again the category is related to
  151. * the severity. The non audit trails can group the log messages
  152. * like: Database (or even the database types), Templates, etc. For
  153. * audit trails it makes much sense to categorize the actions. For
  154. * example: security, modified content, published content, shop, etc.
  155. *
  156. * @package EventLog
  157. * @version //autogentag//
  158. * @mainclass
  159. */
  160. class ezcLog
  161. {
  162. /**
  163. * Debug severity constant.
  164. */
  165. const DEBUG = 1;
  166. /**
  167. * Success audit severity constant.
  168. */
  169. const SUCCESS_AUDIT = 2;
  170. /**
  171. * Failed audit severity constant.
  172. */
  173. const FAILED_AUDIT = 4;
  174. /**
  175. * Info severity constant.
  176. */
  177. const INFO = 8;
  178. /**
  179. * Notice severity constant.
  180. */
  181. const NOTICE = 16;
  182. /**
  183. * Warning severity constant.
  184. */
  185. const WARNING = 32;
  186. /**
  187. * Error severity constant.
  188. */
  189. const ERROR = 64;
  190. /**
  191. * Fatal severity constant.
  192. */
  193. const FATAL = 128;
  194. /**
  195. * Holds the properties of this class.
  196. *
  197. * @var array(string=>mixed)
  198. */
  199. private $properties = array();
  200. /**
  201. * Contains the logic of mapping an incoming log message to the writer.
  202. *
  203. * @var ezcLogFilterSet
  204. */
  205. protected $writers;
  206. /**
  207. * Stores the attributes from the eventTypes and eventSources.
  208. *
  209. * $var ezcLogContext
  210. */
  211. protected $context;
  212. /**
  213. * Stores the instance of this class.
  214. *
  215. * @var ezcLog
  216. */
  217. private static $instance = null;
  218. /**
  219. * Stores the setting whether writer exceptions should be thrown.
  220. *
  221. * @var bool
  222. */
  223. private $throwWriterExceptions = true;
  224. /**
  225. * Constructs an empty ezcLog instance.
  226. *
  227. * This constructor is private as this class should be used as a
  228. * singleton. Use the getInstance() method instead to get an ezcLog instance.
  229. */
  230. private function __construct()
  231. {
  232. $this->reset();
  233. }
  234. /**
  235. * Returns the instance of the class.
  236. *
  237. * @return ezcLog
  238. */
  239. public static function getInstance()
  240. {
  241. if ( is_null( self::$instance ) )
  242. {
  243. self::$instance = new self();
  244. ezcBaseInit::fetchConfig( 'ezcInitLog', self::$instance );
  245. }
  246. return self::$instance;
  247. }
  248. /**
  249. * Sets the property $name to $value.
  250. *
  251. * @throws ezcBasePropertyNotFoundException
  252. * If the property $name does not exist
  253. * @param string $name
  254. * @param mixed $value
  255. * @ignore
  256. */
  257. public function __set( $name, $value )
  258. {
  259. switch ( $name )
  260. {
  261. case "source":
  262. case "category":
  263. $this->properties[$name] = $value;
  264. return;
  265. }
  266. throw new ezcBasePropertyNotFoundException( $name );
  267. }
  268. /**
  269. * Returns the property $name.
  270. *
  271. * @throws ezcBasePropertyNotFoundException
  272. * If the property $name does not exist
  273. * @param string $name
  274. * @return mixed
  275. * @ignore
  276. */
  277. public function __get( $name )
  278. {
  279. switch ( $name )
  280. {
  281. case "source":
  282. case "category":
  283. return $this->properties[$name];
  284. }
  285. throw new ezcBasePropertyNotFoundException( $name );
  286. }
  287. /**
  288. * Returns true if the property $name is set, otherwise false.
  289. *
  290. * @param string $name
  291. * @return bool
  292. * @ignore
  293. */
  294. public function __isset( $name )
  295. {
  296. switch ( $name )
  297. {
  298. case 'source':
  299. case 'category':
  300. return isset( $this->properties[$name] );
  301. default:
  302. return false;
  303. }
  304. }
  305. /**
  306. * Resets the log instance to its initial state.
  307. *
  308. * All sourceAttributes, severityAttributes, and writers will be removed.
  309. * The default source and category are also reset.
  310. */
  311. public function reset()
  312. {
  313. $this->writers = new ezcLogFilterSet();
  314. $this->context = new ezcLogContext();
  315. $this->setDefaults();
  316. }
  317. /**
  318. * Sets the given ezcLogMapper $mapper as the log message to writer map.
  319. *
  320. * By default the ezcLogFilterSet is the default writer map. The default
  321. * ezcLogMapper can be replaced with this method.
  322. *
  323. * @param ezcLogMapper $mapper
  324. */
  325. public function setMapper( ezcLogMapper $mapper )
  326. {
  327. $this->writers = $mapper;
  328. }
  329. /**
  330. * Returns an instance of the current ezcLogMapper.
  331. *
  332. * @return ezcLogMapper
  333. */
  334. public function getMapper()
  335. {
  336. return $this->writers;
  337. }
  338. /**
  339. * Sets the source and category defaults to "default".
  340. */
  341. protected function setDefaults()
  342. {
  343. $this->properties['source'] = "default";
  344. $this->properties['category'] = "default";
  345. }
  346. /**
  347. * Enables or disables writer exceptions with the boolean $enable.
  348. *
  349. * Typically you want to have exceptions enabled while developing your application
  350. * in order to catch potential problems. A live server however, should not throw
  351. * a deadly exception when a relatively unimportant debug message could not be written to
  352. * the log file. For these setups you can disable writer exceptions.
  353. *
  354. * @param bool $enable
  355. */
  356. public function throwWriterExceptions( $enable )
  357. {
  358. $this->throwWriterExceptions = $enable;
  359. }
  360. /**
  361. * Write the message $message with additional information to one or multiple log writers.
  362. *
  363. * The log message $message, severity $severity, and extra attributes $attributes are sent to
  364. * the writers that matches with the {@link ezcLogFilter}. The following parameters are
  365. * taken in the comparation with the ezcLogFilter:
  366. * - $severity: the severity of the log message.
  367. * - $attributes[ "source" ]: the source from where the log message comes.
  368. * - $attributes[ "category" ]: the category of the log message.
  369. *
  370. * See for more information about filter matching the classes {@link ezcLog} and
  371. * {@link ezcLogFilter}.
  372. *
  373. * The message $message describes what happened. The severity $severity is one of the ezcLog constants:
  374. * - DEBUG: Records information about the progress in the program and references
  375. * source code functions. Knowledge of the source code is needed to interpret
  376. * this log message.
  377. * - INFO: Informative logging at a detailed level. This logging method produces a
  378. * high level of logging, which is unmanageable on a production environment.
  379. * Usually INFO logging is only enabled to help by analysing a problem.
  380. * - NOTICE: Informative logging at a lower detail level than INFO logging.
  381. * Only major stages are recorded and is useful to monitor a low volume system.
  382. * - WARNING: Something unexpected happened, but did not cause any loss of service.
  383. * - ERROR: An error occured, which may cause partial loss of service. Usually the
  384. * system can recover.
  385. * - FATAL: An serious error occured and the system is unlikely to recover.
  386. * - SUCCESS_AUDIT: Informative logging about a successful completion of work by
  387. * a module completed. Useful to trace system changes directly or indirectly
  388. * done by a user.
  389. * - FAILED_AUDIT: Informative logging about an action from a module
  390. * with a negative result. A failed login will most likely added to this severity.
  391. *
  392. * The attributes array $attributes can have one or multiple attributes that will
  393. * be added to the log. If source and category are given, they will override the default
  394. * source or category given as property to this object. Further more it is up to the
  395. * application what to include in the log. It may be useful to add the
  396. * file and linenumber to the attributes array. Use the magic PHP constants: {@link __FILE__}
  397. * and {@link __LINE__} for this purpose. The next example adds an warning to the log.
  398. *
  399. * <code>
  400. * ezcLog::getInstance()->source = "templateEngine"; // Set the default source.
  401. * ezcLog::getInstance()->log( "ezcPersistentObject <$obj> does not exist.",
  402. * ezcLog::WARNING,
  403. * array( "category" => "Database", "line" => __LINE__, "file" => __FILE__, "code" => 123 )
  404. * );
  405. * </code>
  406. *
  407. * The methods {@link setSeverityAttributes()} and {@link setSourceAttributes()} can automatically
  408. * add attributes to log messages based on, respectively, the severity and source.
  409. *
  410. * See also {@link logHandler()} on how to use {@link trigger_error()} to write log messages.
  411. *
  412. * @throws ezcLogWriterException if {@link throwWriterExceptions} are enabled and a log entry
  413. * could not be written.
  414. *
  415. * @param string $message
  416. * @param int $severity One of the following severity constants:
  417. * DEBUG, SUCCES_AUDIT, FAIL_AUDIT, INFO, NOTICE, WARNING, ERROR, or FATAL.
  418. * @param array(string=>string) $attributes
  419. */
  420. public function log( $message, $severity, array $attributes = array() )
  421. {
  422. $source = ( isset( $attributes["source"] ) ? $attributes["source"] : $this->properties["source"] );
  423. $category = ( isset( $attributes["category"] ) ? $attributes["category"] : $this->properties["category"] );
  424. unset( $attributes["source"] );
  425. unset( $attributes["category"] );
  426. $attributes = array_merge( $this->context->getContext( $severity, $source ), $attributes );
  427. $writers = $this->writers->get( $severity, $source, $category );
  428. foreach ( $writers as $writer )
  429. {
  430. try
  431. {
  432. $writer->writeLogMessage( $message, $severity, $source, $category, $attributes );
  433. }
  434. catch ( ezcLogWriterException $e )
  435. {
  436. if ( $this->throwWriterExceptions )
  437. {
  438. throw $e;
  439. }
  440. }
  441. }
  442. }
  443. /**
  444. * Sets the attributes $attributes for a group of severities $severityMask.
  445. *
  446. * The severities are specified with a bit mask. These attributes will be
  447. * added to the log message when the log severity is the same as specified
  448. * here.
  449. *
  450. * Example:
  451. * <code>
  452. * ezcLog::getInstance()->setSeverityAttributes(
  453. * ezcLog::SUCCESS_AUDIT | ezcLog::FAILED_AUDIT
  454. * array( "username" => "Jan K. Doodle" )
  455. * );
  456. * </code>
  457. *
  458. * Every log message that has the severity SUCCESS_AUDIT or FAILED_AUDIT
  459. * includes the user name: "Jan K. Doodle".
  460. *
  461. * @param integer $severityMask Multiple severities are specified with a logic-or.
  462. * @param array(string=>string) $attributes
  463. */
  464. public function setSeverityAttributes( $severityMask, $attributes )
  465. {
  466. $this->context->setSeverityContext( $severityMask, $attributes );
  467. }
  468. /**
  469. * Sets the attributes $attributes for a group of sources $sources.
  470. *
  471. * The sources are specified in an array. These attributes will be added to the
  472. * log message when it matches with the given $sources.
  473. *
  474. * Example:
  475. * <code>
  476. * ezcLog::getInstance()->setSourceAttributes(
  477. * array( "Paynet", "Bibit", "Paypal" ),
  478. * array( "MerchantID" => $merchantID )
  479. * );
  480. * </code>
  481. *
  482. * Every log message that comes from the payment module: Paynet, Bibit, or Paypal
  483. * includes the Merchant ID.
  484. *
  485. * @param array(string) $sources
  486. * @param array(string=>string) $attributes
  487. */
  488. public function setSourceAttributes ( $sources, $attributes )
  489. {
  490. $this->context->setSourceContext( $sources, $attributes );
  491. }
  492. /**
  493. * This method can be set as error_handler to log using {@link trigger_error()}.
  494. *
  495. * This method can be assigned with the {@link set_error_handler()} to handle the
  496. * trigger_error calls. This method will get the log instance and forward the
  497. * message. But includes the following information:
  498. * - The file and linenumber are automatically added.
  499. * - Source and category can be 'encoded' in the message.
  500. *
  501. * The message format is as follows:
  502. * <pre>
  503. * [ source, category ] Message
  504. * </pre>
  505. *
  506. * When one name is given between the brackets, the category will be set and the message has a default source:
  507. * <pre>
  508. * [ category ] Message
  509. * </pre>
  510. *
  511. * Without any names between the brackets, the default category and source are used:
  512. * <pre>
  513. * Message
  514. * </pre>
  515. *
  516. * The following example creates manually an error handler and forwards the
  517. * ERROR, WARNING and NOTICE severities.
  518. * <code>
  519. * function myLogHandler($errno, $errstr, $errfile, $errline)
  520. * {
  521. * switch ($errno)
  522. * {
  523. * case E_USER_ERROR:
  524. * case E_USER_WARNING:
  525. * case E_USER_NOTICE:
  526. * if ( $loggingEnabled )
  527. * { // Forward the message to the log handler.
  528. * ezcLog::LogHandler( $errno, $errstr, $errfile, $errline );
  529. * }
  530. * break;
  531. *
  532. * default:
  533. * print( "$errstr in $errfile on line $errline\n" );
  534. * break;
  535. * }
  536. * }
  537. *
  538. * // Register myLogHandler
  539. * set_error_handler( "myLogHandler" );
  540. *
  541. * // Write an warning to the log.
  542. * trigger_error( "[paynet, transaction] Didn't get a callback from the Paynet service", E_USER_WARNING );
  543. *
  544. * // Add a notice.
  545. * trigger_error( "Getting paynet status information", E_USER_NOTICE );
  546. *
  547. * </code>
  548. *
  549. * Notice that the ezcLog component is not loaded at all when the logging is disabled.
  550. *
  551. * @param int $errno
  552. * @param int $errstr
  553. * @param string $errfile
  554. * @param int $errline
  555. */
  556. public static function logHandler( $errno, $errstr, $errfile, $errline )
  557. {
  558. $log = ezcLog::getInstance();
  559. $lm = new ezcLogMessage( $errstr, $errno, $log->source, $log->category );
  560. $log->log(
  561. $lm->message, $lm->severity,
  562. array( "source" => $lm->source, "category" => $lm->category, "file" => $errfile, "line" => $errline )
  563. );
  564. }
  565. /**
  566. * Translates the severity constant to a string and returns this.
  567. *
  568. * Null is returned when the severity constant is invalid.
  569. *
  570. * @param int $severity
  571. * @return string
  572. */
  573. public static function translateSeverityName( $severity )
  574. {
  575. switch ( $severity )
  576. {
  577. case self::DEBUG: return "Debug";
  578. case self::SUCCESS_AUDIT: return "Success audit";
  579. case self::FAILED_AUDIT: return "Failed audit";
  580. case self::INFO: return "Info";
  581. case self::NOTICE: return "Notice";
  582. case self::WARNING: return "Warning";
  583. case self::ERROR: return "Error";
  584. case self::FATAL: return "Fatal";
  585. default: return null;
  586. }
  587. }
  588. }
  589. ?>