Log.php 5.1 KB

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