IntrospectionProcessor.php 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. <?php
  2. /*
  3. * This file is part of the Monolog package.
  4. *
  5. * (c) Jordi Boggiano <j.boggiano@seld.be>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Monolog\Processor;
  11. use Monolog\Logger;
  12. /**
  13. * Injects line/file:class/function where the log message came from
  14. *
  15. * Warning: This only works if the handler processes the logs directly.
  16. * If you put the processor on a handler that is behind a FingersCrossedHandler
  17. * for example, the processor will only be called once the trigger level is reached,
  18. * and all the log records will have the same file/line/.. data from the call that
  19. * triggered the FingersCrossedHandler.
  20. *
  21. * @author Jordi Boggiano <j.boggiano@seld.be>
  22. */
  23. class IntrospectionProcessor
  24. {
  25. private $level;
  26. private $skipClassesPartials;
  27. private $skipStackFramesCount;
  28. private $skipFunctions = array(
  29. 'call_user_func',
  30. 'call_user_func_array',
  31. );
  32. public function __construct($level = Logger::DEBUG, array $skipClassesPartials = array(), $skipStackFramesCount = 0)
  33. {
  34. $this->level = Logger::toMonologLevel($level);
  35. $this->skipClassesPartials = array_merge(array('Monolog\\'), $skipClassesPartials);
  36. $this->skipStackFramesCount = $skipStackFramesCount;
  37. }
  38. /**
  39. * @param array $record
  40. * @return array
  41. */
  42. public function __invoke(array $record)
  43. {
  44. // return if the level is not high enough
  45. if ($record['level'] < $this->level) {
  46. return $record;
  47. }
  48. /*
  49. * http://php.net/manual/en/function.debug-backtrace.php
  50. * As of 5.3.6, DEBUG_BACKTRACE_IGNORE_ARGS option was added.
  51. * Any version less than 5.3.6 must use the DEBUG_BACKTRACE_IGNORE_ARGS constant value '2'.
  52. */
  53. $trace = debug_backtrace((PHP_VERSION_ID < 50306) ? 2 : DEBUG_BACKTRACE_IGNORE_ARGS);
  54. // skip first since it's always the current method
  55. array_shift($trace);
  56. // the call_user_func call is also skipped
  57. array_shift($trace);
  58. $i = 0;
  59. while ($this->isTraceClassOrSkippedFunction($trace, $i)) {
  60. if (isset($trace[$i]['class'])) {
  61. foreach ($this->skipClassesPartials as $part) {
  62. if (strpos($trace[$i]['class'], $part) !== false) {
  63. $i++;
  64. continue 2;
  65. }
  66. }
  67. } elseif (in_array($trace[$i]['function'], $this->skipFunctions)) {
  68. $i++;
  69. continue;
  70. }
  71. break;
  72. }
  73. $i += $this->skipStackFramesCount;
  74. // we should have the call source now
  75. $record['extra'] = array_merge(
  76. $record['extra'],
  77. array(
  78. 'file' => isset($trace[$i - 1]['file']) ? $trace[$i - 1]['file'] : null,
  79. 'line' => isset($trace[$i - 1]['line']) ? $trace[$i - 1]['line'] : null,
  80. 'class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : null,
  81. 'function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : null,
  82. )
  83. );
  84. return $record;
  85. }
  86. private function isTraceClassOrSkippedFunction(array $trace, $index)
  87. {
  88. if (!isset($trace[$index])) {
  89. return false;
  90. }
  91. return isset($trace[$index]['class']) || in_array($trace[$index]['function'], $this->skipFunctions);
  92. }
  93. }