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