[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/ -> Logger.php (source)

   1  <?php
   2  
   3  namespace dokuwiki;
   4  
   5  use dokuwiki\Extension\Event;
   6  
   7  /**
   8   * Log messages to a daily log file
   9   */
  10  class Logger
  11  {
  12      public const LOG_ERROR = 'error';
  13      public const LOG_DEPRECATED = 'deprecated';
  14      public const LOG_DEBUG = 'debug';
  15  
  16      /** @var Logger[] */
  17      protected static $instances;
  18  
  19      /** @var string what kind of log is this */
  20      protected $facility;
  21  
  22      protected $isLogging = true;
  23  
  24      /**
  25       * Logger constructor.
  26       *
  27       * @param string $facility The type of log
  28       */
  29      protected function __construct($facility)
  30      {
  31          global $conf;
  32          $this->facility = $facility;
  33  
  34          // Should logging be disabled for this facility?
  35          $dontlog = explode(',', $conf['dontlog']);
  36          $dontlog = array_map('trim', $dontlog);
  37          if (in_array($facility, $dontlog)) $this->isLogging = false;
  38      }
  39  
  40      /**
  41       * Return a Logger instance for the given facility
  42       *
  43       * @param string $facility The type of log
  44       * @return Logger
  45       */
  46      public static function getInstance($facility = self::LOG_ERROR)
  47      {
  48          if (empty(self::$instances[$facility])) {
  49              self::$instances[$facility] = new Logger($facility);
  50          }
  51          return self::$instances[$facility];
  52      }
  53  
  54      /**
  55       * Convenience method to directly log to the error log
  56       *
  57       * @param string $message The log message
  58       * @param mixed $details Any details that should be added to the log entry
  59       * @param string $file A source filename if this is related to a source position
  60       * @param int $line A line number for the above file
  61       * @return bool has a log been written?
  62       */
  63      public static function error($message, $details = null, $file = '', $line = 0)
  64      {
  65          return self::getInstance(self::LOG_ERROR)->log(
  66              $message,
  67              $details,
  68              $file,
  69              $line
  70          );
  71      }
  72  
  73      /**
  74       * Convenience method to directly log to the debug log
  75       *
  76       * @param string $message The log message
  77       * @param mixed $details Any details that should be added to the log entry
  78       * @param string $file A source filename if this is related to a source position
  79       * @param int $line A line number for the above file
  80       * @return bool has a log been written?
  81       */
  82      public static function debug($message, $details = null, $file = '', $line = 0)
  83      {
  84          return self::getInstance(self::LOG_DEBUG)->log(
  85              $message,
  86              $details,
  87              $file,
  88              $line
  89          );
  90      }
  91  
  92      /**
  93       * Convenience method to directly log to the deprecation log
  94       *
  95       * @param string $message The log message
  96       * @param mixed $details Any details that should be added to the log entry
  97       * @param string $file A source filename if this is related to a source position
  98       * @param int $line A line number for the above file
  99       * @return bool has a log been written?
 100       */
 101      public static function deprecated($message, $details = null, $file = '', $line = 0)
 102      {
 103          return self::getInstance(self::LOG_DEPRECATED)->log(
 104              $message,
 105              $details,
 106              $file,
 107              $line
 108          );
 109      }
 110  
 111      /**
 112       * Log a message to the facility log
 113       *
 114       * @param string $message The log message
 115       * @param mixed $details Any details that should be added to the log entry
 116       * @param string $file A source filename if this is related to a source position
 117       * @param int $line A line number for the above file
 118       * @triggers LOGGER_DATA_FORMAT can be used to change the logged data or intercept it
 119       * @return bool has a log been written?
 120       */
 121      public function log($message, $details = null, $file = '', $line = 0)
 122      {
 123          global $EVENT_HANDLER;
 124          if (!$this->isLogging) return false;
 125  
 126          $datetime = time();
 127          $data = [
 128              'facility' => $this->facility,
 129              'datetime' => $datetime,
 130              'message' => $message,
 131              'details' => $details,
 132              'file' => $file,
 133              'line' => $line,
 134              'loglines' => [],
 135              'logfile' => $this->getLogfile($datetime),
 136          ];
 137  
 138          if ($EVENT_HANDLER !== null) {
 139              $event = new Event('LOGGER_DATA_FORMAT', $data);
 140              if ($event->advise_before()) {
 141                  $data['loglines'] = $this->formatLogLines($data);
 142              }
 143              $event->advise_after();
 144          } else {
 145              // The event system is not yet available, to ensure the log isn't lost even on
 146              // fatal errors, the default action is executed
 147              $data['loglines'] = $this->formatLogLines($data);
 148          }
 149  
 150          // only log when any data available
 151          if (count($data['loglines'])) {
 152              return $this->writeLogLines($data['loglines'], $data['logfile']);
 153          } else {
 154              return false;
 155          }
 156      }
 157  
 158      /**
 159       * Is this logging instace actually logging?
 160       *
 161       * @return bool
 162       */
 163      public function isLogging()
 164      {
 165          return $this->isLogging;
 166      }
 167  
 168      /**
 169       * Formats the given data as loglines
 170       *
 171       * @param array $data Event data from LOGGER_DATA_FORMAT
 172       * @return string[] the lines to log
 173       */
 174      protected function formatLogLines($data)
 175      {
 176          extract($data);
 177  
 178          // details are logged indented
 179          if ($details) {
 180              if (!is_string($details)) {
 181                  $details = json_encode($details, JSON_PRETTY_PRINT);
 182              }
 183              $details = explode("\n", $details);
 184              $loglines = array_map(static fn($line) => '  ' . $line, $details);
 185          } elseif ($details) {
 186              $loglines = [$details];
 187          } else {
 188              $loglines = [];
 189          }
 190  
 191          // datetime, fileline, message
 192          $logline = gmdate('Y-m-d H:i:s', $datetime) . "\t";
 193          if ($file) {
 194              $logline .= $file;
 195              if ($line) $logline .= "($line)";
 196          }
 197          $logline .= "\t" . $message;
 198          array_unshift($loglines, $logline);
 199  
 200          return $loglines;
 201      }
 202  
 203      /**
 204       * Construct the log file for the given day
 205       *
 206       * @param false|string|int $date Date to access, false for today
 207       * @return string
 208       */
 209      public function getLogfile($date = false)
 210      {
 211          global $conf;
 212  
 213          if ($date !== null && !is_numeric($date)) {
 214              $date = strtotime($date);
 215          }
 216          if (!$date) $date = time();
 217  
 218          return $conf['logdir'] . '/' . $this->facility . '/' . date('Y-m-d', $date) . '.log';
 219      }
 220  
 221      /**
 222       * Write the given lines to today's facility log
 223       *
 224       * @param string[] $lines the raw lines to append to the log
 225       * @param string $logfile where to write to
 226       * @return bool true if the log was written
 227       */
 228      protected function writeLogLines($lines, $logfile)
 229      {
 230          if (defined('DOKU_UNITTEST')) {
 231              fwrite(STDERR, "\n[" . $this->facility . '] ' . implode("\n", $lines) . "\n");
 232          }
 233          return io_saveFile($logfile, implode("\n", $lines) . "\n", true);
 234      }
 235  }