Log.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. <?php
  2. /**
  3. * Message logging with observer-based log writing.
  4. *
  5. * [!!] This class does not support extensions, only additional writers.
  6. *
  7. * @package Kohana
  8. * @category Logging
  9. * @author Kohana Team
  10. * @copyright (c) Kohana Team
  11. * @license https://koseven.ga/LICENSE.md
  12. */
  13. class Kohana_Log {
  14. // Log message levels - Windows users see PHP Bug #18090
  15. const EMERGENCY = LOG_EMERG; // 0
  16. const ALERT = LOG_ALERT; // 1
  17. const CRITICAL = LOG_CRIT; // 2
  18. const ERROR = LOG_ERR; // 3
  19. const WARNING = LOG_WARNING; // 4
  20. const NOTICE = LOG_NOTICE; // 5
  21. const INFO = LOG_INFO; // 6
  22. const DEBUG = LOG_DEBUG; // 7
  23. /**
  24. * @var boolean immediately write when logs are added
  25. */
  26. public static $write_on_add = FALSE;
  27. /**
  28. * @var Log Singleton instance container
  29. */
  30. protected static $_instance;
  31. /**
  32. * Get the singleton instance of this class and enable writing at shutdown.
  33. *
  34. * $log = Log::instance();
  35. *
  36. * @return Log
  37. */
  38. public static function instance()
  39. {
  40. if (Log::$_instance === NULL)
  41. {
  42. // Create a new instance
  43. Log::$_instance = new Log;
  44. // Write the logs at shutdown
  45. register_shutdown_function([Log::$_instance, 'write']);
  46. }
  47. return Log::$_instance;
  48. }
  49. /**
  50. * @var array list of added messages
  51. */
  52. protected $_messages = [];
  53. /**
  54. * @var array list of log writers
  55. */
  56. protected $_writers = [];
  57. /**
  58. * Attaches a log writer, and optionally limits the levels of messages that
  59. * will be written by the writer.
  60. *
  61. * $log->attach($writer);
  62. *
  63. * @param Log_Writer $writer instance
  64. * @param mixed $levels array of messages levels to write OR max level to write
  65. * @param integer $min_level min level to write IF $levels is not an array
  66. * @return Log
  67. */
  68. public function attach(Log_Writer $writer, $levels = [], $min_level = 0)
  69. {
  70. if ( ! is_array($levels))
  71. {
  72. $levels = range($min_level, $levels);
  73. }
  74. $this->_writers["{$writer}"] = [
  75. 'object' => $writer,
  76. 'levels' => $levels
  77. ];
  78. return $this;
  79. }
  80. /**
  81. * Detaches a log writer. The same writer object must be used.
  82. *
  83. * $log->detach($writer);
  84. *
  85. * @param Log_Writer $writer instance
  86. * @return Log
  87. */
  88. public function detach(Log_Writer $writer)
  89. {
  90. // Remove the writer
  91. unset($this->_writers["{$writer}"]);
  92. return $this;
  93. }
  94. /**
  95. * Adds a message to the log. Replacement values must be passed in to be
  96. * replaced using [strtr](http://php.net/strtr).
  97. *
  98. * $log->add(Log::ERROR, 'Could not locate user: :user', array(
  99. * ':user' => $username,
  100. * ));
  101. *
  102. * @param string $level level of message
  103. * @param string $message message body
  104. * @param array $values values to replace in the message
  105. * @param array $additional additional custom parameters to supply to the log writer
  106. * @return Log
  107. */
  108. public function add($level, $message, array $values = NULL, array $additional = NULL)
  109. {
  110. if ($values)
  111. {
  112. // Insert the values into the message
  113. $message = strtr($message, $values);
  114. }
  115. // Grab a copy of the trace
  116. if (isset($additional['exception']))
  117. {
  118. $trace = $additional['exception']->getTrace();
  119. }
  120. else
  121. {
  122. // Older php version don't have 'DEBUG_BACKTRACE_IGNORE_ARGS', so manually remove the args from the backtrace
  123. if ( ! defined('DEBUG_BACKTRACE_IGNORE_ARGS'))
  124. {
  125. $trace = array_map(function ($item) {
  126. unset($item['args']);
  127. return $item;
  128. }, array_slice(debug_backtrace(FALSE), 1));
  129. }
  130. else
  131. {
  132. $trace = array_slice(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), 1);
  133. }
  134. }
  135. if ($additional == NULL)
  136. {
  137. $additional = [];
  138. }
  139. // Create a new message
  140. $this->_messages[] = [
  141. 'time' => time(),
  142. 'level' => $level,
  143. 'body' => $message,
  144. 'trace' => $trace,
  145. 'file' => isset($trace[0]['file']) ? $trace[0]['file'] : NULL,
  146. 'line' => isset($trace[0]['line']) ? $trace[0]['line'] : NULL,
  147. 'class' => isset($trace[0]['class']) ? $trace[0]['class'] : NULL,
  148. 'function' => isset($trace[0]['function']) ? $trace[0]['function'] : NULL,
  149. 'additional' => $additional,
  150. ];
  151. if (Log::$write_on_add)
  152. {
  153. // Write logs as they are added
  154. $this->write();
  155. }
  156. return $this;
  157. }
  158. /**
  159. * Write and clear all of the messages.
  160. *
  161. * $log->write();
  162. *
  163. * @return void
  164. */
  165. public function write()
  166. {
  167. if (empty($this->_messages))
  168. {
  169. // There is nothing to write, move along
  170. return;
  171. }
  172. // Import all messages locally
  173. $messages = $this->_messages;
  174. // Reset the messages array
  175. $this->_messages = [];
  176. foreach ($this->_writers as $writer)
  177. {
  178. if (empty($writer['levels']))
  179. {
  180. // Write all of the messages
  181. $writer['object']->write($messages);
  182. }
  183. else
  184. {
  185. // Filtered messages
  186. $filtered = [];
  187. foreach ($messages as $message)
  188. {
  189. if (in_array($message['level'], $writer['levels']))
  190. {
  191. // Writer accepts this kind of message
  192. $filtered[] = $message;
  193. }
  194. }
  195. // Write the filtered messages
  196. $writer['object']->write($filtered);
  197. }
  198. }
  199. }
  200. }