PageRenderTime 81ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/Lampcms/Log.php

https://github.com/snytkine/LampCMS
PHP | 414 lines | 130 code | 62 blank | 222 comment | 19 complexity | 3be3199ced566bcbbc93add9aa7afebe MD5 | raw file
Possible License(s): LGPL-3.0
  1. <?php
  2. /**
  3. *
  4. * License, TERMS and CONDITIONS
  5. *
  6. * This software is licensed under the GNU LESSER GENERAL PUBLIC LICENSE (LGPL) version 3
  7. * Please read the license here : http://www.gnu.org/licenses/lgpl-3.0.txt
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted provided that the following conditions are met:
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. * 3. The name of the author may not be used to endorse or promote products
  17. * derived from this software without specific prior written permission.
  18. *
  19. * ATTRIBUTION REQUIRED
  20. * 4. All web pages generated by the use of this software, or at least
  21. * the page that lists the recent questions (usually home page) must include
  22. * a link to the http://www.lampcms.com and text of the link must indicate that
  23. * the website's Questions/Answers functionality is powered by lampcms.com
  24. * An example of acceptable link would be "Powered by <a href="http://www.lampcms.com">LampCMS</a>"
  25. * The location of the link is not important, it can be in the footer of the page
  26. * but it must not be hidden by style attributes
  27. *
  28. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
  29. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  30. * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  31. * IN NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
  32. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  33. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  34. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  35. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  36. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  37. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  38. *
  39. * This product includes GeoLite data created by MaxMind,
  40. * available from http://www.maxmind.com/
  41. *
  42. *
  43. * @author Dmitri Snytkine <cms@lampcms.com>
  44. * @copyright 2005-2012 (or current year) Dmitri Snytkine
  45. * @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU LESSER GENERAL PUBLIC LICENSE (LGPL) version 3
  46. * @link http://www.lampcms.com Lampcms.com project
  47. * @version Release: @package_version@
  48. *
  49. *
  50. */
  51. namespace Lampcms;
  52. /**
  53. * Public static logger class
  54. * This class is responsible for logging
  55. * messages to a file
  56. *
  57. * @author Dmitri Snytkine
  58. *
  59. */
  60. class Log
  61. {
  62. const PREFIX_ERROR = '[ERROR] ';
  63. const PREFIX_INFO = '[INFO]';
  64. const PREFIX_DEBUG = '[DEBUG]';
  65. /**
  66. * Location of log file
  67. * it must point to actual file
  68. * and that file must be writable to php program
  69. * (usually this means writable to apache server)
  70. *
  71. * @var string
  72. */
  73. const LOG_FILE_PATH = '';
  74. /**
  75. *
  76. * You may hard-code the email of developer
  77. * then you don't need to define the same value in !config.ini
  78. * This way even if email is not defined in !config.ini
  79. * developers will still receive notifications
  80. * of all errors logged via the e() function
  81. *
  82. * @var string
  83. */
  84. const LAMPCMS_DEVELOPER_EMAIL = '';
  85. /**
  86. * Format of timestamp
  87. *
  88. * @var string
  89. */
  90. const TIME_FORMAT = "F j, Y H:i:s";
  91. /**
  92. * String to be used as a subject line
  93. * of email notification
  94. *
  95. * @var string
  96. */
  97. const EMAIL_SUBJECT = 'Error on your website';
  98. /**
  99. * Our main logging function
  100. *
  101. * @param string $message message to log
  102. * @param int $traceLevel this is useful
  103. * for extracting correct line from debug backtrace
  104. * you should normally not worry about this
  105. * This is useful in only some cases where you notice that
  106. * line number/method name is not logged correctly
  107. *
  108. * @param string $logType
  109. *
  110. * @return string message that was just logged
  111. */
  112. public static function l($message, $traceLevel = 0, $logType = self::DEBUG_LEVEL)
  113. {
  114. $logPath = self::getLogPath();
  115. if (empty($logPath)) {
  116. return;
  117. }
  118. /**
  119. * automatically stringify array
  120. * in case we want to just add array to log
  121. */
  122. $str = (\is_array($message)) ? \json_encode($message, true) : $message;
  123. $string = '';
  124. $line = 'unknown';
  125. /**
  126. * Passing the false as param
  127. * will reduce the size of backtrace object, sometimes considerably
  128. * because by default, this value is true and it means
  129. * that each object of backtrace is dumped!
  130. *
  131. */
  132. $arrBacktrace = \debug_backtrace(false);
  133. /**
  134. * Special case: if the ->log() called from an object
  135. * that does not directly extend this class but has the __call() method
  136. * then the __call() would pass the log() to the upstream object (this object)
  137. *
  138. * in such case the level 1 will be the __call method itself
  139. * and the level 2 will be the actual method that invoked the __call
  140. *
  141. * In a case like this we are interested in level2 of backtrace!
  142. */
  143. if (\array_key_exists(1, $arrBacktrace)) {
  144. if ('__call' === $arrBacktrace[1]['function']) {
  145. $traceLevel += 2;
  146. } elseif ('call_user_func_array' === $arrBacktrace[1]['function']) {
  147. $traceLevel += 3;
  148. }
  149. }
  150. $level1 = $traceLevel + 1;
  151. if (!empty($arrBacktrace[$level1])) {
  152. if (!empty($arrBacktrace[$level1]['class'])) {
  153. $string .= $arrBacktrace[$level1]['class'];
  154. $gotClass = true;
  155. if (!empty($arrBacktrace[$level1]['type'])) {
  156. $string .= $arrBacktrace[$level1]['type'];
  157. }
  158. }
  159. if (!empty($arrBacktrace[$level1]['function'])) {
  160. $string .= $arrBacktrace[$level1]['function'] . '() ';
  161. }
  162. }
  163. if (!empty($arrBacktrace[$traceLevel])) {
  164. if (!isset($gotClass) && !empty($arrBacktrace[$traceLevel]['file'])) {
  165. $string .= $arrBacktrace[$traceLevel]['file'] . ' ';
  166. }
  167. if (!empty($arrBacktrace[$traceLevel]['line'])) {
  168. $line = $arrBacktrace[$traceLevel]['line'];
  169. }
  170. $string .= '[' . $line . '] ';
  171. }
  172. //$string .= PHP_EOL . $str;
  173. $string .= $str;
  174. $message = PHP_EOL . self::getTimeStamp() . ' ' . $logType . ' ' . $string;
  175. $res = \file_put_contents($logPath, $message, FILE_APPEND | LOCK_EX);
  176. return $message;
  177. }
  178. /**
  179. * Log debug message. The debug messages
  180. * are NOT logged in normal production environment
  181. * Debugging messages are logged
  182. * ONLY when global constant LAMPCMS_DEBUG is set to true
  183. *
  184. *
  185. * @param string $message message to log
  186. * @param int $level
  187. *
  188. * @return string
  189. */
  190. public static function d($message, $level = 0)
  191. {
  192. /**
  193. * Increase backtrace level to one
  194. * to account to delegating from this
  195. * method to log() method
  196. */
  197. return self::l($message, ++$level, self::PREFIX_DEBUG);
  198. }
  199. /**
  200. * Log debug message. The debug messages
  201. * are NOT logged in normal production environment
  202. * Debugging messages are logged
  203. * ONLY when global constant LAMPCMS_DEBUG is set to true
  204. *
  205. *
  206. * @param string $message message to log
  207. * @param int $level
  208. *
  209. * @return string
  210. */
  211. public static function info($message, $level = 0)
  212. {
  213. /**
  214. * Increase backtrace level to one
  215. * to account to delegating from this
  216. * method to log() method
  217. */
  218. return self::l($message, ++$level, self::PREFIX_INFO);
  219. }
  220. /**
  221. * Log error message. The main difference
  222. * between using this method and normal log()
  223. * is that email will also be sent to admin
  224. *
  225. * @param string $message message to log
  226. * @param int $level debug backtrace offset level
  227. *
  228. * @return string
  229. */
  230. public static function e($message, $level = 0)
  231. {
  232. /**
  233. * Increase backtrace level to one
  234. * to account to delegating from this
  235. * method to log() method
  236. */
  237. $message = self::l($message, ++$level, self::PREFIX_ERROR);
  238. self::notifyDeveloper($message);
  239. return $message;
  240. }
  241. /**
  242. * Get path to log file
  243. * If global constant LOG_FILE_PATH is defined
  244. * then use it, otherwise use
  245. * this class's constant
  246. *
  247. * @return string a path to log file
  248. *
  249. */
  250. protected static function getLogPath()
  251. {
  252. if (defined('SPECIAL_LOG_FILE')) {
  253. return SPECIAL_LOG_FILE;
  254. }
  255. return (defined('LOG_FILE_PATH')) ? LOG_FILE_PATH : self::LOG_FILE_PATH;
  256. }
  257. /**
  258. * Sends email message to developer
  259. * if message contains error pattern
  260. *
  261. * @param string $message message to send to developers
  262. *
  263. * @return void
  264. */
  265. protected static function notifyDeveloper($message)
  266. {
  267. global $Mailer;
  268. $devEmail = self::getDevEmail();
  269. if (empty($devEmail)) {
  270. return;
  271. }
  272. $msg = $message;
  273. $ua = self::getServerVar('HTTP_USER_AGENT');
  274. /**
  275. * Do NOT send out any errors generated from MSIE 6.0 browsers
  276. *
  277. */
  278. if (isset($_SERVER) && is_array($_SERVER) && (false === \strstr($ua, 'MSIE 6.0'))) {
  279. $msg .= "\n" . '-----------------------------------------------------';
  280. $msg .= "\n" . 'HTTP_HOST: ' . self::getServerVar('HTTP_HOST');
  281. $msg .= "\n" . 'SCRIPT_NAME: ' . self::getServerVar('SCRIPT_NAME');
  282. $msg .= "\n" . 'REQUEST_METHOD: ' . self::getServerVar('REQUEST_METHOD');
  283. $msg .= "\n" . 'REQUEST_URI: ' . self::getServerVar('REQUEST_URI');
  284. $msg .= "\n" . 'SCRIPT_FILENAME: ' . self::getServerVar('SCRIPT_FILENAME');
  285. $msg .= "\n" . '-----------------------------------------------------';
  286. $msg .= "\n" . 'HTTP_USER_AGENT: ' . $ua;
  287. $msg .= "\n" . 'HTTP_REFERER: ' . self::getServerVar('HTTP_REFERER');
  288. $msg .= "\n" . '-----------------------------------------------------';
  289. $msg .= "\n" . 'REMOTE_ADDR/IP: ' . self::getServerVar('REMOTE_ADDR');
  290. $msg .= PHP_EOL . 'REQUEST HEADERS: ' . Request::getAllHeadersAsString();
  291. if (Request::isPost()) {
  292. $msg .= "\n" . '-----------------------------------------------------';
  293. $msg .= "\n" . 'POST: ' . \print_r($_POST, true);
  294. }
  295. /**
  296. * Add high priority to email headers
  297. * for error messages of certain types (real errors, no notices)
  298. */
  299. $headers = 'X-Mailer: LogObserver' . "\n" . 'X-Priority: 1' . "\n" . 'Importance: High' . "\n" . 'X-MSMail-Priority: High';
  300. /**
  301. * Attempt to use Mailer object, then fallback to php's mail()
  302. */
  303. $ER = error_reporting(0);
  304. if (is_object($Mailer)) {
  305. try {
  306. $Mailer->mail($devEmail, self::EMAIL_SUBJECT, $msg, null, false);
  307. } catch ( \Exception $e ) {
  308. @mail($devEmail, self::EMAIL_SUBJECT, $msg, $headers);
  309. }
  310. } else {
  311. @mail($devEmail, self::EMAIL_SUBJECT, $msg, $headers);
  312. }
  313. error_reporting($ER);
  314. }
  315. return;
  316. }
  317. /**
  318. * Get value from global $_SERVER array
  319. * if it exists, otherwise return just an empty string
  320. *
  321. * @param string $var
  322. *
  323. * @return string value of $var or empty string
  324. */
  325. protected static function getServerVar($var)
  326. {
  327. return (\array_key_exists($var, $_SERVER)) ? $_SERVER[$var] : '';
  328. }
  329. /**
  330. * Get string representation of
  331. * current timestamp
  332. *
  333. * @return string a formatted timestamp
  334. */
  335. protected static function getTimeStamp()
  336. {
  337. return date(self::TIME_FORMAT) . ' ';
  338. }
  339. /**
  340. * Get email address of developer
  341. * if global constant LAMPCMS_DEVELOPER_EMAIL exists
  342. * then return it, otherwise return this class's
  343. * self::LAMPCMS_DEVELOPER_EMAIL
  344. *
  345. *
  346. * @return string email address of developer
  347. */
  348. protected static function getDevEmail()
  349. {
  350. return defined('LAMPCMS_DEVELOPER_EMAIL') ? LAMPCMS_DEVELOPER_EMAIL : self::LAMPCMS_DEVELOPER_EMAIL;
  351. }
  352. }