log.inc 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. <?php
  2. /**
  3. * @file
  4. * Logging classes for the database layer.
  5. */
  6. /**
  7. * Database query logger.
  8. *
  9. * We log queries in a separate object rather than in the connection object
  10. * because we want to be able to see all queries sent to a given database, not
  11. * database target. If we logged the queries in each connection object we
  12. * would not be able to track what queries went to which target.
  13. *
  14. * Every connection has one and only one logging object on it for all targets
  15. * and logging keys.
  16. */
  17. class DatabaseLog {
  18. /**
  19. * Cache of logged queries. This will only be used if the query logger is enabled.
  20. *
  21. * The structure for the logging array is as follows:
  22. *
  23. * array(
  24. * $logging_key = array(
  25. * array(query => '', args => array(), caller => '', target => '', time => 0),
  26. * array(query => '', args => array(), caller => '', target => '', time => 0),
  27. * ),
  28. * );
  29. *
  30. * @var array
  31. */
  32. protected $queryLog = array();
  33. /**
  34. * The connection key for which this object is logging.
  35. *
  36. * @var string
  37. */
  38. protected $connectionKey = 'default';
  39. /**
  40. * Constructor.
  41. *
  42. * @param $key
  43. * The database connection key for which to enable logging.
  44. */
  45. public function __construct($key = 'default') {
  46. $this->connectionKey = $key;
  47. }
  48. /**
  49. * Begin logging queries to the specified connection and logging key.
  50. *
  51. * If the specified logging key is already running this method does nothing.
  52. *
  53. * @param $logging_key
  54. * The identification key for this log request. By specifying different
  55. * logging keys we are able to start and stop multiple logging runs
  56. * simultaneously without them colliding.
  57. */
  58. public function start($logging_key) {
  59. if (empty($this->queryLog[$logging_key])) {
  60. $this->clear($logging_key);
  61. }
  62. }
  63. /**
  64. * Retrieve the query log for the specified logging key so far.
  65. *
  66. * @param $logging_key
  67. * The logging key to fetch.
  68. * @return
  69. * An indexed array of all query records for this logging key.
  70. */
  71. public function get($logging_key) {
  72. return $this->queryLog[$logging_key];
  73. }
  74. /**
  75. * Empty the query log for the specified logging key.
  76. *
  77. * This method does not stop logging, it simply clears the log. To stop
  78. * logging, use the end() method.
  79. *
  80. * @param $logging_key
  81. * The logging key to empty.
  82. */
  83. public function clear($logging_key) {
  84. $this->queryLog[$logging_key] = array();
  85. }
  86. /**
  87. * Stop logging for the specified logging key.
  88. *
  89. * @param $logging_key
  90. * The logging key to stop.
  91. */
  92. public function end($logging_key) {
  93. unset($this->queryLog[$logging_key]);
  94. }
  95. /**
  96. * Log a query to all active logging keys.
  97. *
  98. * @param $statement
  99. * The prepared statement object to log.
  100. * @param $args
  101. * The arguments passed to the statement object.
  102. * @param $time
  103. * The time in milliseconds the query took to execute.
  104. */
  105. public function log(DatabaseStatementInterface $statement, $args, $time) {
  106. foreach (array_keys($this->queryLog) as $key) {
  107. $this->queryLog[$key][] = array(
  108. 'query' => $statement->getQueryString(),
  109. 'args' => $args,
  110. 'target' => $statement->dbh->getTarget(),
  111. 'caller' => $this->findCaller(),
  112. 'time' => $time,
  113. );
  114. }
  115. }
  116. /**
  117. * Determine the routine that called this query.
  118. *
  119. * We define "the routine that called this query" as the first entry in
  120. * the call stack that is not inside includes/database and does have a file
  121. * (which excludes call_user_func_array(), anonymous functions and similar).
  122. * That makes the climbing logic very simple, and handles the variable stack
  123. * depth caused by the query builders.
  124. *
  125. * @link http://www.php.net/debug_backtrace
  126. * @return
  127. * This method returns a stack trace entry similar to that generated by
  128. * debug_backtrace(). However, it flattens the trace entry and the trace
  129. * entry before it so that we get the function and args of the function that
  130. * called into the database system, not the function and args of the
  131. * database call itself.
  132. */
  133. public function findCaller() {
  134. $stack = debug_backtrace();
  135. $stack_count = count($stack);
  136. for ($i = 0; $i < $stack_count; ++$i) {
  137. if (!empty($stack[$i]['file']) && strpos($stack[$i]['file'], 'includes' . DIRECTORY_SEPARATOR . 'database') === FALSE) {
  138. $stack[$i] += array('args' => array());
  139. return array(
  140. 'file' => $stack[$i]['file'],
  141. 'line' => $stack[$i]['line'],
  142. 'function' => $stack[$i + 1]['function'],
  143. 'class' => isset($stack[$i + 1]['class']) ? $stack[$i + 1]['class'] : NULL,
  144. 'type' => isset($stack[$i + 1]['type']) ? $stack[$i + 1]['type'] : NULL,
  145. 'args' => $stack[$i + 1]['args'],
  146. );
  147. }
  148. }
  149. }
  150. }