1 | <?php |
||
2 | /** |
||
3 | * @package midcom |
||
4 | * @author The Midgard Project, http://www.midgard-project.org |
||
5 | * @copyright The Midgard Project, http://www.midgard-project.org |
||
6 | * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License |
||
7 | */ |
||
8 | |||
9 | use Symfony\Component\VarDumper\Cloner\VarCloner; |
||
10 | use Symfony\Component\VarDumper\Dumper\CliDumper; |
||
11 | use Monolog\Logger; |
||
12 | use Symfony\Component\HttpKernel\Event\RequestEvent; |
||
13 | use Symfony\Component\HttpKernel\Event\TerminateEvent; |
||
14 | |||
15 | /** |
||
16 | * This is a debugger class. |
||
17 | * |
||
18 | * Helps in debugging your code. It lets you decide which messages are logged into the |
||
19 | * logfile by setting loglevels for the debugger and for each message. |
||
20 | * |
||
21 | * There are five loglevel constants you can use when setting the loglevel or when |
||
22 | * logging messages: |
||
23 | * |
||
24 | * - MIDCOM_LOG_DEBUG |
||
25 | * - MIDCOM_LOG_INFO |
||
26 | * - MIDCOM_LOG_WARN |
||
27 | * - MIDCOM_LOG_ERROR |
||
28 | * - MIDCOM_LOG_CRIT |
||
29 | * |
||
30 | * @package midcom |
||
31 | */ |
||
32 | class midcom_debug |
||
33 | { |
||
34 | private Logger $logger; |
||
35 | |||
36 | /** |
||
37 | * Standard constructor |
||
38 | */ |
||
39 | 3 | public function __construct(Logger $logger) |
|
40 | { |
||
41 | 3 | $this->logger = $logger; |
|
42 | } |
||
43 | |||
44 | 352 | public function on_request(RequestEvent $event) |
|
45 | { |
||
46 | 352 | if ($event->isMainRequest()) { |
|
47 | $this->log("Start of MidCOM run " . $event->getRequest()->server->get('REQUEST_URI', '')); |
||
48 | } |
||
49 | } |
||
50 | |||
51 | public function on_terminate(TerminateEvent $event) |
||
52 | { |
||
53 | $this->log("End of MidCOM run: " . $event->getRequest()->server->get('REQUEST_URI')); |
||
54 | } |
||
55 | |||
56 | /** |
||
57 | * Converts MidCOM log levels to Monolog |
||
58 | */ |
||
59 | 701 | public static function convert_level(int $level) : int |
|
60 | { |
||
61 | 701 | $level_map = [ |
|
62 | 701 | MIDCOM_LOG_DEBUG => Logger::DEBUG, |
|
63 | 701 | MIDCOM_LOG_INFO => Logger::INFO, |
|
64 | 701 | MIDCOM_LOG_WARN => Logger::WARNING, |
|
65 | 701 | MIDCOM_LOG_ERROR => Logger::ERROR, |
|
66 | 701 | MIDCOM_LOG_CRIT => Logger::CRITICAL |
|
67 | 701 | ]; |
|
68 | 701 | return $level_map[$level] ?? $level; |
|
69 | } |
||
70 | |||
71 | public function log_php_error(int $loglevel) |
||
72 | { |
||
73 | $error = error_get_last(); |
||
74 | if (!empty($error['message'])) { |
||
75 | $this->log('Last PHP error was: ' . $error['message'], $loglevel); |
||
76 | } |
||
77 | } |
||
78 | |||
79 | /** |
||
80 | * Log a message |
||
81 | */ |
||
82 | 700 | public function log(string $message, int $loglevel = MIDCOM_LOG_DEBUG) |
|
83 | { |
||
84 | 700 | $bt = array_slice(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), 1); |
|
85 | 700 | $this->logger->pushProcessor(function(array $record) use ($bt) { |
|
86 | 700 | return $this->get_caller($record, $bt); |
|
87 | 700 | }); |
|
88 | 700 | $this->logger->addRecord(self::convert_level($loglevel), trim($message)); |
|
89 | 700 | $this->logger->popProcessor(); |
|
90 | } |
||
91 | |||
92 | /** |
||
93 | * @internal |
||
94 | */ |
||
95 | 703 | public function get_caller(array $record, array $bt) : array |
|
96 | { |
||
97 | 703 | $record['extra']['caller'] = ''; |
|
98 | 703 | while ($bt) { |
|
99 | 703 | $caller = array_shift($bt); |
|
100 | 703 | if (!in_array($caller['class'] ?? '', [midcom_debug::class])) { |
|
101 | 703 | break; |
|
102 | } |
||
103 | } |
||
104 | |||
105 | 703 | $record['extra']['caller'] .= $caller['file'] . ':' . $caller['line'] . ''; |
|
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
![]() |
|||
106 | 703 | return $record; |
|
107 | } |
||
108 | |||
109 | /** |
||
110 | * Dump a variable |
||
111 | */ |
||
112 | 25 | public function print_r(string $message, $variable, int $loglevel = MIDCOM_LOG_DEBUG) |
|
113 | { |
||
114 | 25 | $this->logger->pushProcessor(function(array $record) use ($variable) { |
|
115 | 25 | $cloner = new VarCloner(); |
|
116 | 25 | $dumper = new CliDumper(); |
|
117 | 25 | $record['message'] .= ' ' . $dumper->dump($cloner->cloneVar($variable), true); |
|
118 | 25 | return $record; |
|
119 | 25 | }); |
|
120 | 25 | $this->log($message, $loglevel); |
|
121 | 25 | $this->logger->popProcessor(); |
|
122 | } |
||
123 | |||
124 | /** |
||
125 | * Dump stack trace |
||
126 | */ |
||
127 | 26 | public function print_function_stack(string $message, int $loglevel = MIDCOM_LOG_DEBUG) |
|
128 | { |
||
129 | 26 | $this->logger->pushProcessor(function(array $record) { |
|
130 | // the last four levels are already inside the debugging system, so skip those |
||
131 | 26 | $stack = array_slice(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), 4); |
|
132 | 26 | $stacktrace = ""; |
|
133 | 26 | foreach ($stack as $number => $frame) { |
|
134 | 26 | $stacktrace .= $number + 1; |
|
135 | 26 | if (isset($frame['file'])) { |
|
136 | 26 | $stacktrace .= ": {$frame['file']}:{$frame['line']} "; |
|
137 | } |
||
138 | 26 | if (array_key_exists('class', $frame)) { |
|
139 | 26 | $stacktrace .= "{$frame['class']}::{$frame['function']}"; |
|
140 | 26 | } elseif (array_key_exists('function', $frame)) { |
|
141 | 26 | $stacktrace .= $frame['function']; |
|
142 | } else { |
||
143 | $stacktrace .= 'require, include or eval'; |
||
144 | } |
||
145 | 26 | $stacktrace .= "\n"; |
|
146 | } |
||
147 | |||
148 | 26 | $record['message'] .= "\n{$stacktrace}"; |
|
149 | 26 | return $record; |
|
150 | 26 | }); |
|
151 | 26 | $this->log($message, $loglevel); |
|
152 | 26 | $this->logger->popProcessor(); |
|
153 | } |
||
154 | } |
||
155 |