PageRenderTime 26ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/wire/core/WireLog.php

http://github.com/ryancramerdesign/ProcessWire
PHP | 362 lines | 157 code | 35 blank | 170 comment | 31 complexity | cd4dde44fdbb94554966f0a5be0a8b46 MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception
  1. <?php
  2. /**
  3. * ProcessWire Log
  4. *
  5. * WireLog represents the ProcessWire $log API variable.
  6. * It is an API-friendly interface to the FileLog class.
  7. *
  8. * ProcessWire 2.x
  9. * Copyright (C) 2013 by Ryan Cramer
  10. * This file licensed under Mozilla Public License v2.0 http://mozilla.org/MPL/2.0/
  11. *
  12. * https://processwire.com
  13. *
  14. */
  15. class WireLog extends Wire {
  16. protected $logExtension = 'txt';
  17. /**
  18. * Record an informational or 'success' message in the message log (messages.txt)
  19. *
  20. * @param string $text Message to log
  21. * @param bool|int $flags Specify boolean true to also have the message displayed interactively (admin only).
  22. * @return $this
  23. *
  24. */
  25. public function message($text, $flags = 0) {
  26. $flags = $flags === true ? Notice::log : $flags | Notice::logOnly;
  27. return parent::message($text, $flags);
  28. }
  29. /**
  30. * Record an error message in the error log (errors.txt)
  31. *
  32. * Note: Fatal errors should instead throw a WireException.
  33. *
  34. * @param string $text
  35. * @param int|bool $flags Specify boolean true to also display the error interactively (admin only).
  36. * @return $this
  37. *
  38. */
  39. public function error($text, $flags = 0) {
  40. $flags = $flags === true ? Notice::log : $flags | Notice::logOnly;
  41. return parent::error($text, $flags);
  42. }
  43. /**
  44. * Record a warning message in the warnings log (warnings.txt)
  45. *
  46. * @param string $text
  47. * @param int|bool $flags Specify boolean true to also display the warning interactively (admin only).
  48. * @return $this
  49. *
  50. */
  51. public function warning($text, $flags = 0) {
  52. $flags = $flags === true ? Notice::log : $flags | Notice::logOnly;
  53. return parent::warning($text, $flags);
  54. }
  55. /**
  56. * Save text to a caller-defined log
  57. *
  58. * If the log doesn't currently exist, it will be created.
  59. *
  60. * The log filename is /site/assets/logs/[name].txt
  61. *
  62. * @param string $name Name of log to save to (word consisting of [-._a-z0-9] and no extension)
  63. * @param string $text Text to save to the log
  64. * @param array $options Options to modify behavior (defaults: showUser=true, showURL=true, url='url', delimiter=\t)
  65. * @return bool Whether it was written (usually assumed to be true)
  66. * @throws WireException
  67. *
  68. */
  69. public function ___save($name, $text, $options = array()) {
  70. $defaults = array(
  71. 'showUser' => true,
  72. 'showURL' => true,
  73. 'url' => '', // URL to show (default=blank, auto-detect)
  74. 'delimiter' => "\t",
  75. );
  76. $options = array_merge($defaults, $options);
  77. // showURL option was previously named showPage
  78. if(isset($options['showPage'])) $options['showURL'] = $options['showPage'];
  79. $log = $this->getFileLog($name, $options);
  80. $text = str_replace(array("\r", "\n", "\t"), ' ', $text);
  81. if($options['showURL']) {
  82. if($options['url']) {
  83. $url = $options['url'];
  84. } else {
  85. $input = $this->wire('input');
  86. $url = $input ? $input->httpUrl() : '';
  87. if(strlen($url) && $input) {
  88. if(count($input->get)) {
  89. $url .= "?";
  90. foreach($input->get as $k => $v) {
  91. $k = $this->wire('sanitizer')->name($k);
  92. $v = $this->wire('sanitizer')->name($v);
  93. $url .= "$k=$v&";
  94. }
  95. $url = rtrim($url, "&");
  96. }
  97. if(strlen($url) > 500) $url = substr($url, 0, 500) . " ...";
  98. } else {
  99. $url = '?';
  100. }
  101. }
  102. $text = "$url$options[delimiter]$text";
  103. }
  104. if($options['showUser']) {
  105. $user = $this->wire('user');
  106. $text = ($user && $user->id ? $user->name : "?") . "$options[delimiter]$text";
  107. }
  108. return $log->save($text);
  109. }
  110. /**
  111. * Return array of all logs
  112. *
  113. * Each log entry is an array that includes the following:
  114. * - name (string): Name of log file, excluding extension.
  115. * - file (string): Full path and filename of log file.
  116. * - size (int): Size in bytes
  117. * - modified (int): Last modified date (unix timestamp)
  118. *
  119. * @return array
  120. *
  121. */
  122. public function getLogs() {
  123. $logs = array();
  124. $dir = new DirectoryIterator($this->wire('config')->paths->logs);
  125. foreach($dir as $file) {
  126. if($file->isDot() || $file->isDir()) continue;
  127. if($file->getExtension() != 'txt') continue;
  128. $name = basename($file, '.txt');
  129. if($name != $this->wire('sanitizer')->pageName($name)) continue;
  130. $logs[$name] = array(
  131. 'name' => $name,
  132. 'file' => $file->getPathname(),
  133. 'size' => $file->getSize(),
  134. 'modified' => $file->getMTime(),
  135. );
  136. }
  137. ksort($logs);
  138. return $logs;
  139. }
  140. /**
  141. * Get the full filename (including path) for the given log name
  142. *
  143. * @param string $name
  144. * @return string
  145. * @throws WireException
  146. *
  147. */
  148. public function getFilename($name) {
  149. if($name !== $this->wire('sanitizer')->pageName($name)) {
  150. throw new WireException("Log name must contain only [-_.a-z0-9] with no extension");
  151. }
  152. return $this->wire('config')->paths->logs . $name . '.' . $this->logExtension;
  153. }
  154. /**
  155. * Return the given number of entries from the end of log file
  156. *
  157. * This method is pagination aware.
  158. *
  159. * @param string $name Name of log
  160. * @param array $options Specify any of the following:
  161. * - limit (integer): Specify number of lines.
  162. * - text (string): Text to find.
  163. * - dateFrom (int|string): Oldest date to match entries.
  164. * - dateTo (int|string): Newest date to match entries.
  165. * - reverse (bool): Reverse order (default=true)
  166. * - pageNum (int): Pagination number 1 or above (default=0 which means auto-detect)
  167. * @return array
  168. *
  169. */
  170. public function getLines($name, array $options = array()) {
  171. $pageNum = !empty($options['pageNum']) ? $options['pageNum'] : $this->wire('input')->pageNum;
  172. unset($options['pageNum']);
  173. $log = $this->getFileLog($name);
  174. $limit = isset($options['limit']) ? (int) $options['limit'] : 100;
  175. return $log->find($limit, $pageNum);
  176. }
  177. /**
  178. * Same as getLines() but returns each log line as an associative array of each part of the line split up
  179. *
  180. * @param $name Name of log file (excluding extension)
  181. * @param array $options
  182. * - limit (integer): Specify number of lines.
  183. * - text (string): Text to find.
  184. * - dateFrom (int|string): Oldest date to match entries.
  185. * - dateTo (int|string): Newest date to match entries.
  186. * - reverse (bool): Reverse order (default=true)
  187. * - pageNum (int): Pagination number 1 or above (default=0 which means auto-detect)
  188. * @return array(array(
  189. * 'date' => "ISO-8601 date string",
  190. * 'user' => "user name or boolean false if unknown",
  191. * 'url' => "full URL or boolean false if unknown",
  192. * 'text' => "text of the log entry"
  193. * ));
  194. *
  195. */
  196. public function getEntries($name, array $options = array()) {
  197. $log = $this->getFileLog($name);
  198. $limit = isset($options['limit']) ? $options['limit'] : 100;
  199. $pageNum = !empty($options['pageNum']) ? $options['pageNum'] : $this->wire('input')->pageNum;
  200. unset($options['pageNum']);
  201. $lines = $log->find($limit, $pageNum, $options);
  202. foreach($lines as $key => $line) {
  203. $entry = $this->lineToEntry($line);
  204. $lines[$key] = $entry;
  205. }
  206. return $lines;
  207. }
  208. /**
  209. * Convert a log line to an entry array
  210. *
  211. * @param $line
  212. * @return array
  213. *
  214. */
  215. public function lineToEntry($line) {
  216. $parts = explode("\t", $line, 4);
  217. if(count($parts) == 2) {
  218. $entry = array(
  219. 'date' => $parts[0],
  220. 'user' => '',
  221. 'url' => '',
  222. 'text' => $parts[1]
  223. );
  224. } else if(count($parts) == 3) {
  225. $entry = array(
  226. 'date' => $parts[0],
  227. 'user' => strpos($parts[1], '/') === false ? $parts[1] : '',
  228. 'url' => strpos($parts[1], '/') !== false ? $parts[1] : '',
  229. 'text' => $parts[2]
  230. );
  231. } else {
  232. $entry = array(
  233. 'date' => isset($parts[0]) ? $parts[0] : '',
  234. 'user' => isset($parts[1]) ? $parts[1] : '',
  235. 'url' => isset($parts[2]) ? $parts[2] : '',
  236. 'text' => isset($parts[3]) ? $parts[3] : '',
  237. );
  238. }
  239. $entry['date'] = wireDate(wire('config')->dateFormat, strtotime($entry['date']));
  240. $entry['user'] = wire('sanitizer')->pageName($entry['user']);
  241. if($entry['url'] == 'page?') $entry['url'] = false;
  242. if($entry['user'] == 'user?') $entry['user'] = false;
  243. return $entry;
  244. }
  245. /**
  246. * Get the total number of entries present in the given log
  247. *
  248. * @param $name
  249. * @return int
  250. *
  251. */
  252. public function getTotalEntries($name) {
  253. $log = $this->getFileLog($name);
  254. return $log->getTotalLines();
  255. }
  256. /**
  257. * Get lines from log file (deprecated)
  258. *
  259. * @param $name
  260. * @param int $limit
  261. * @param array $options
  262. * @deprecated Use getLines() or getEntries() intead.
  263. * @return array
  264. *
  265. */
  266. public function get($name, $limit = 100, array $options = array()) {
  267. return $this->getLines($name, $limit, $options);
  268. }
  269. /**
  270. * Return an array of log lines that exist in the given range of dates
  271. *
  272. * Pagination aware.
  273. *
  274. * @param string $name Name of log
  275. * @param int|string $dateFrom Unix timestamp or string date/time to start from
  276. * @param int|string $dateTo Unix timestamp or string date/time to end at (default = now)
  277. * @param int $limit Max items per pagination
  278. * @return array
  279. * @deprecated Use getLines() or getEntries() with dateFrom/dateTo $options instead.
  280. *
  281. */
  282. public function getDate($name, $dateFrom, $dateTo = 0, $limit = 100) {
  283. $log = $this->getFileLog($name);
  284. $pageNum = $this->wire('input')->pageNum();
  285. return $log->getDate($dateFrom, $dateTo, $pageNum, $limit);
  286. }
  287. /**
  288. * Delete the log file identified by $name
  289. *
  290. * @param $name
  291. * @return bool
  292. *
  293. */
  294. public function delete($name) {
  295. $log = $this->getFileLog($name);
  296. return $log->delete();
  297. }
  298. /**
  299. * Prune log file to contain only entries from last n days
  300. *
  301. * @param string $name
  302. * @param int $days
  303. * @return int Number of items in new log file or booean false on failure
  304. * @throws WireException
  305. *
  306. */
  307. public function prune($name, $days) {
  308. $log = $this->getFileLog($name);
  309. if($days < 1) throw new WireException("Prune days must be 1 or more");
  310. $oldestDate = strtotime("-$days DAYS");
  311. return $log->pruneDate($oldestDate);
  312. }
  313. /**
  314. * Returns instance of FileLog for given log name
  315. *
  316. * @param $name
  317. * @param array $options
  318. * @return FileLog
  319. *
  320. */
  321. public function getFileLog($name, array $options = array()) {
  322. $log = new FileLog($this->getFilename($name));
  323. if(isset($options['delimiter'])) $log->setDelimeter($options['delimiter']);
  324. else $log->setDelimeter("\t");
  325. return $log;
  326. }
  327. }