PageRenderTime 65ms CodeModel.GetById 35ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/main/lib/diag/logformatter.php

https://gitlab.com/alexprowars/bitrix
PHP | 319 lines | 239 code | 32 blank | 48 comment | 25 complexity | 67c75835b6ebbcd9a21789c527f64923 MD5 | raw file
  1. <?php
  2. /**
  3. * Bitrix Framework
  4. * @package bitrix
  5. * @subpackage main
  6. * @copyright 2001-2021 Bitrix
  7. */
  8. namespace Bitrix\Main\Diag;
  9. use Bitrix\Main\Type;
  10. use Bitrix\Main\DB;
  11. class LogFormatter implements LogFormatterInterface
  12. {
  13. protected const DELIMITER = '----------';
  14. protected $showArguments;
  15. protected $argMaxChars;
  16. public function __construct($showArguments = false, $argMaxChars = 30)
  17. {
  18. $this->showArguments = $showArguments;
  19. $this->argMaxChars = $argMaxChars;
  20. }
  21. /**
  22. * Basic formatter to a string. Supports placeholders: {exception}, {trace}, {date}, {delimiter}.
  23. * @inheritDoc
  24. */
  25. public function format($message, array $context = []): string
  26. {
  27. // Implementors MAY have special handling for the passed objects. If that is not the case, implementors MUST cast it to a string.
  28. $message = $this->castToString($message);
  29. if (!isset($context['delimiter']))
  30. {
  31. $context['delimiter'] = static::DELIMITER;
  32. }
  33. // Placeholder names MUST be delimited with a single opening brace { and a single closing brace }. There MUST NOT be any whitespace between the delimiters and the placeholder name.
  34. $replace = [];
  35. foreach ($context as $key => $val)
  36. {
  37. $replace['{' . $key . '}'] = $this->castToString($val, $key);
  38. }
  39. return strtr($message, $replace);
  40. }
  41. /**
  42. * Magic is here.
  43. * @param mixed $value
  44. * @param null $placeholder
  45. * @return string
  46. */
  47. protected function castToString($value, $placeholder = null): string
  48. {
  49. if (!is_string($value))
  50. {
  51. if (is_object($value))
  52. {
  53. if ($placeholder == 'date' && $value instanceof Type\Date)
  54. {
  55. $value = $this->formatDate($value);
  56. }
  57. elseif ($placeholder == 'exception' && $value instanceof \Throwable)
  58. {
  59. $value = $this->formatException($value);
  60. }
  61. elseif (method_exists($value, '__toString'))
  62. {
  63. $value = (string)$value;
  64. }
  65. else
  66. {
  67. $value = $this->formatMixed($value);
  68. }
  69. }
  70. else
  71. {
  72. if ($placeholder == 'trace' && is_array($value))
  73. {
  74. $value = $this->formatTrace($value);
  75. }
  76. else
  77. {
  78. $value = $this->formatMixed($value);
  79. }
  80. }
  81. }
  82. return $value;
  83. }
  84. /**
  85. * Formats an exception.
  86. * @param \Throwable $exception
  87. * @return string
  88. */
  89. protected function formatException(\Throwable $exception): string
  90. {
  91. $result = '[' . get_class($exception) . '] ';
  92. if ($exception instanceof \ErrorException)
  93. {
  94. $result .= static::severityToString($exception->getSeverity());
  95. }
  96. $result .= "\n" . $exception->getMessage() . ' (' . $exception->getCode() . ')' . "\n";
  97. if ($exception instanceof DB\SqlQueryException)
  98. {
  99. $result .= $exception->getQuery() . "\n";
  100. }
  101. $file = $exception->getFile();
  102. if ($file)
  103. {
  104. $result .= $file . ':' . $exception->getLine() . "\n";
  105. }
  106. return $result;
  107. }
  108. /**
  109. * Formats a backtrace array.
  110. * @param array $trace
  111. * @return string
  112. */
  113. protected function formatTrace(array $trace): string
  114. {
  115. $result = '';
  116. foreach ($trace as $traceNum => $traceInfo)
  117. {
  118. $traceLine = '#'.$traceNum.': ';
  119. if (isset($traceInfo['class']))
  120. {
  121. $traceLine .= $traceInfo['class'] . $traceInfo['type'];
  122. }
  123. if (isset($traceInfo['function']))
  124. {
  125. $traceLine .= $traceInfo['function'];
  126. if(isset($traceInfo['args']))
  127. {
  128. $traceLine .= $this->formatArguments($traceInfo['args']);
  129. }
  130. }
  131. $traceLine .= "\n\t";
  132. if (isset($traceInfo['file']))
  133. {
  134. $traceLine .= $traceInfo['file'] . ':' . $traceInfo['line'];
  135. }
  136. $result .= $traceLine . "\n";
  137. }
  138. return $result;
  139. }
  140. /**
  141. * @internal
  142. * @param $args
  143. * @return string
  144. */
  145. public function formatArguments($args)
  146. {
  147. if ($args !== null)
  148. {
  149. $arguments = [];
  150. foreach ($args as $arg)
  151. {
  152. $arguments[] = $this->formatArgument($arg);
  153. }
  154. return '(' . implode(', ', $arguments) . ')';
  155. }
  156. return '()';
  157. }
  158. /**
  159. * @internal
  160. * @param $arg
  161. * @return array|string|string[]
  162. */
  163. public function formatArgument($arg)
  164. {
  165. if (!$this->showArguments)
  166. {
  167. return gettype($arg);
  168. }
  169. switch (gettype($arg))
  170. {
  171. case 'boolean':
  172. $result = $arg ? 'true' : 'false';
  173. break;
  174. case 'NULL':
  175. $result = 'null';
  176. break;
  177. case 'integer':
  178. case 'double':
  179. case 'float':
  180. $result = (string)$arg;
  181. break;
  182. case 'string':
  183. if (is_callable($arg, false, $callableName))
  184. {
  185. $result = 'fs:' . $callableName;
  186. }
  187. elseif (class_exists($arg, false))
  188. {
  189. $result = 'c:' . $arg;
  190. }
  191. elseif (interface_exists($arg, false))
  192. {
  193. $result = 'i:' . $arg;
  194. }
  195. else
  196. {
  197. if (mb_strlen($arg) > $this->argMaxChars)
  198. {
  199. $result = '"' . mb_substr($arg, 0, $this->argMaxChars / 2) . '...' . mb_substr($arg, -$this->argMaxChars / 2) . '" (' . mb_strlen($arg) . ')';
  200. }
  201. else
  202. {
  203. $result = '"' . $arg . '"';
  204. }
  205. }
  206. break;
  207. case 'array':
  208. if (is_callable($arg, false, $callableName))
  209. {
  210. $result = 'fa:' . $callableName;
  211. }
  212. else
  213. {
  214. $result = 'array(' . count($arg) . ')';
  215. }
  216. break;
  217. case 'object':
  218. $result = '[' . get_class($arg) . ']';
  219. break;
  220. case 'resource':
  221. $result = 'r:' . get_resource_type($arg);
  222. break;
  223. default:
  224. $result = 'unknown type';
  225. break;
  226. }
  227. return str_replace("\n", '\n', $result);
  228. }
  229. /**
  230. * Formats a date.
  231. * @param Type\DateTime $dateTime
  232. * @return string
  233. */
  234. protected function formatDate(Type\Date $dateTime): string
  235. {
  236. return $dateTime->format('Y-m-d H:i:s');
  237. }
  238. /**
  239. * Formats a mixed value.
  240. * @param mixed $value
  241. * @return string
  242. */
  243. protected function formatMixed($value): string
  244. {
  245. return var_export($value, true);
  246. }
  247. public static function severityToString($severity)
  248. {
  249. switch ($severity)
  250. {
  251. case 1:
  252. return 'E_ERROR';
  253. case 2:
  254. return 'E_WARNING';
  255. case 4:
  256. return 'E_PARSE';
  257. case 8:
  258. return 'E_NOTICE';
  259. case 16:
  260. return 'E_CORE_ERROR';
  261. case 32:
  262. return 'E_CORE_WARNING';
  263. case 64:
  264. return 'E_COMPILE_ERROR';
  265. case 128:
  266. return 'E_COMPILE_WARNING';
  267. case 256:
  268. return 'E_USER_ERROR';
  269. case 512:
  270. return 'E_USER_WARNING';
  271. case 1024:
  272. return 'E_USER_NOTICE';
  273. case 2048:
  274. return 'E_STRICT';
  275. case 4096:
  276. return 'E_RECOVERABLE_ERROR';
  277. case 8192:
  278. return 'E_DEPRECATED';
  279. case 16384:
  280. return 'E_USER_DEPRECATED';
  281. case 30719:
  282. return 'E_ALL';
  283. default:
  284. return 'UNKNOWN';
  285. }
  286. }
  287. }