source: trunk/common/log.php @ 997

Revision 997, 10.8 KB checked in by strat.cristian@…, 3 years ago (diff)

Log remote IP and X-Forwarded-For header on job submit, textblock create/edit, and attachment upload. For privacy considerations, IP addresses are only displayed to admins and helpers.

  • Contains database migration script.
  • Adds new simplified security action. (sensitive-info)
  • Property svn:eol-style set to native
Line 
1<?php
2
3// This file contains a simple logging/debug api. You should always use these
4// functions instead of print, print_r and echo.
5//
6// These functions wrap around php's error reporting, generating E_USER
7// messages. Things like whether they are displayed in the output or merely
8// logged is configurable in php.ini.
9//
10// The optional $include_origin parameter determines if the origin of the
11// message (file and line right now) is appended to the message. It defaults
12// to false except for assertions.
13//
14// NOTE: this is for development messages only. Users should never get to
15// see them. There are other functions to deal with bad form values, or
16// insufficient permissions or other usual errors.
17//
18// WARNING: when included this file will change the default error handler.
19// This is good, we never want to run without logging support anyway.
20
21// Private function.
22// Formats a backtrace level as a string.
23// $backtrace is the backtrace as received from a call to debug_backtrace.
24// If ommited debug_backtrace will be called
25//
26// false is returned on error (like level out of range).
27function format_backtrace($level, $backtrace = false) {
28    // Generate backtrace if missing.
29    if ($backtrace === false) {
30        $backtrace = debug_backtrace();
31    }
32
33    // Level out of range.
34    if (!isset($backtrace[$level])) {
35        return false;
36    }
37
38    // Filename. Strips IA_ROOT_DIR.
39    $file = false;
40    if (isset($backtrace[$level]['file'])) {
41        $file = $backtrace[$level]['file'];
42        $file = preg_replace("/^".preg_quote(IA_ROOT_DIR, '/')."/", "", $file);
43    }
44
45    // Source line.
46    $line = false;
47    if (isset($backtrace[$level]['line'])) {
48        $line = $backtrace[$level]['line'];
49    }
50
51    // Function name. Includes class name for methods.
52    $func = false;
53    if (isset($backtrace[$level + 1]) && isset($backtrace[$level + 1]['function'])) {
54        $btl = $backtrace[$level + 1];
55        if (isset($btl['class']) && isset($btl['type'])) {
56            $func = $btl['class'].$btl['type'].$btl['function'];
57        } else {
58            $func = $btl['function'];
59        }
60    }
61    // Don't print these functions.
62    if ($func == 'require_once' || $func == 'require' ||
63        $func == 'include_once' || $func == 'include') {
64        $func = false;
65    }
66
67    $msg = "";
68    if ($func !== false) {
69        $msg .= "function $func ";
70    }
71    if ($file !== false) {
72        $msg .= "file $file ";
73        if ($line !== false) {
74            $msg .= "line $line ";
75        }
76    }
77    return substr($msg, 0, strlen($msg) - 1);
78}
79
80// Private function.
81// Add backtrace info to a message.
82function format_message_backtrace($message, $backtrace_level = 0) {
83    return $message . " in " . format_backtrace($backtrace_level + 2);
84}
85
86// trigger_error which split multi-line strings.
87function trigger_error_split($error_msg, $error_type)
88{
89    if (IA_DEVELOPMENT_MODE && IA_HTTP_ENV) {
90        // FIXME: Un-hack this
91        // when in development mode, also save mesage in log buffer
92        // so it can be displayed to user inside rendering page
93        global $execution_stats;
94        log_assert($execution_stats);
95        $execution_stats['log_copy'] .= $error_msg."\n";
96    }
97
98    $error_msg = (string)$error_msg;
99    $error_lines = explode("\n", $error_msg);
100    foreach ($error_lines as $error_line) {
101        trigger_error($error_line, $error_type);
102    }
103}
104
105// Print a simple message to the log. Use for informative messages.
106function log_print($message, $include_origin = false) {
107    if ($include_origin) {
108        $message = format_message_backtrace($message);
109    }
110    trigger_error_split($message, E_USER_NOTICE);
111}
112
113// Use this for warning messages.
114function log_warn($message, $include_origin = false, $backtrace_level = 0) {
115    if ($include_origin) {
116        $message = format_message_backtrace($message, $backtrace_level);
117    }
118    trigger_error_split($message, E_USER_WARNING);
119}
120
121if (!defined("IA_FROM_SMF")) {
122    // SMF already defines `log_error`. The function signature is *almost*
123    // the same so why not use it? :) PHP style
124
125    // Use this when you hit a serious problem, and can't recover.
126    // You might want to use log_warn() instead.
127    function log_error($message, $include_origin = false) {
128        if ($include_origin) {
129            $message = format_message_backtrace($message);
130        }
131        // Splitting makes no sense for fatal errors because the first line makes
132        // make the script die().
133        trigger_error($message, E_USER_ERROR);
134    }
135}
136
137// Print a complete backtrace, using log_print.
138function log_backtrace($start_level = 0, $backtrace = false, $straight_to_log = false)
139{
140    // Generate backtrace if missing.
141    if ($backtrace === false) {
142        $backtrace = debug_backtrace();
143    }
144
145    // Do the dew.
146    for ($i = $start_level; $i < count($backtrace); ++$i) {
147        $msg = " - Backtrace Level $i: ".format_backtrace($i, $backtrace);
148        if ($straight_to_log) {
149            error_log($msg);
150        } else {
151            log_print($msg, false);
152        }
153    }
154}
155
156// var_dump to the log.
157function log_var_dump($var)
158{
159    ob_start();
160    var_dump($var);
161    $msg = ob_get_clean();
162    log_print($msg);
163}
164
165// print_r to the log.
166function log_print_r($var)
167{
168    $msg = print_r($var, true);
169    log_print($msg);
170}
171
172// Check if $value is true, and if it isn't it prints and error and dies.
173function log_assert($value, $message = "Assertion failed", $include_origin = true) {
174    if (!$value) {
175        if ($include_origin) {
176            $message = format_message_backtrace($message);
177        }
178        log_error($message, false);
179    }
180}
181
182// Assert that something is valid; to be used with form-like validation functions.
183// This passed if $errors is an empty array, other
184function log_assert_valid($errors)
185{
186    log_assert(is_array($errors) || $errors == null, "Invalid argument");
187
188    if ($errors) {
189        foreach ($errors as $k => $v) {
190            log_print("Invalid $k ($v)");
191        }
192        log_error("Validation failed");
193    }
194}
195
196// Check if two values are equal.
197// Prints arguments if it fails, so it's generally a good idea.
198function log_assert_equal($v1, $v2) {
199    if ($v1 != $v2) {
200        $message = format_message_backtrace("$v1 != $v2");
201        if (is_array($v1)) {
202            log_print_r($v1);
203        }
204        if (is_array($v2)) {
205            log_print_r($v2);
206        }
207        log_error($message, false);
208    }
209}
210
211// Custom error_handler.
212// This behaves as close standard error handler as possible, it uses
213// error_log and all. file/line information is not printed for messages
214// originating in the log_* family of functions.
215//
216// FIXME: print function name too? could be done.
217function logging_error_handler($errno, $errstr, $errfile, $errline) {
218    // Obey error_reporting from php_ini.
219    // The @ operator works by changing error_reporting, so it will
220    // work just fine.
221    if ((error_reporting() & $errno) == 0) {
222        return;
223    }
224
225    // Maps error type constant names to message prefixes.
226    $errortypestr_msgprefix = array(
227        'E_ERROR'              => 'PHP Error: ',
228        'E_WARNING'            => 'PHP Warning: ',
229        'E_NOTICE'             => 'PHP Notice: ',
230        'E_PARSE'              => 'Parse Error: ',
231        'E_CORE_ERROR'         => 'Core Error: ',
232        'E_CORE_WARNING'       => 'Core Warning: ',
233        'E_COMPILE_ERROR'      => 'Compile Error: ',
234        'E_COMPILE_WARNING'    => 'Compile Warning: ',
235        'E_STRICT'             => 'Runtime Notice: ',
236        'E_USER_ERROR'         => 'Error: ',
237        'E_USER_WARNING'       => 'Warning: ',
238        'E_USER_NOTICE'        => '',
239        'E_RECOVERABLE_ERRROR' => 'Catchable Fatal Error: ',
240    );
241
242    // Maps error type constant to messages.
243    // Built from errortypestr_message to cleanly handler missing constants in
244    // older php versions.
245    $errortype_msgprefix = array();
246
247    // Build errortype_msgprefix.
248    // FIXME: cache?
249    foreach ($errortypestr_msgprefix as $errortypestr => $message) {
250        if (defined($errortypestr)) {
251            $errortype_msgprefix[constant($errortypestr)] = $message;
252        }
253    }
254
255    // Hack for log_print and all.
256    $include_backtrace = true;
257    if ($errno == E_USER_ERROR || $errno == E_USER_WARNING || $errno == E_USER_NOTICE) {
258        $backtrace = debug_backtrace();
259        // backtrace[0] is the current function, so we check for backtrace[1].
260        if (($errno == E_USER_NOTICE  && $backtrace[1]['function'] = 'log_print') ||
261            ($errno == E_USER_WARNING && $backtrace[1]['function'] = 'log_warn' ) ||
262            ($errno == E_USER_ERROR   && $backtrace[1]['function'] = 'log_error')) {
263            $include_backtrace = false;
264        }
265    }
266
267    if ($include_backtrace) {
268        //$errstr = format_message_backtrace($errstr, 1);
269        $errstr = "$errstr in $errfile line $errline";
270    }
271
272    // Include message prefix.
273    $errstr = $errortype_msgprefix[$errno] . $errstr;
274
275    // HACK: Add timestamp if desired.
276    if (defined('IA_LOG_TIMESTAMP_FORMAT')) {
277        $errstr = date(IA_LOG_TIMESTAMP_FORMAT) . ": " . $errstr;
278    }
279
280    // The behaviour of this function is defined with error_log in php.ini
281    if (IA_PHP_FATAL_ERROR_MASK & $errno) {
282        $errstr = "Fatal:\t>>>" . $errstr . "<<<\t";
283    }
284    error_log($errstr);
285
286    // Die on certain fatal errors:
287    if (IA_PHP_FATAL_ERROR_MASK & $errno) {
288        // Print a full backtrace on fatal errors.
289        error_log("Caught a fatal error, printing a full backtrace");
290        log_backtrace(2, false, true);
291
292        if (IA_HTTP_ENV && IA_DEVELOPMENT_MODE) {
293            $msg = $errstr."\nPrinting full backtrace:\n";
294            $backtrace = debug_backtrace();
295            for ($i = 1; $i < count($backtrace); ++$i) {
296                $msg .= "Backtrace Level $i: ";
297                $msg .= format_backtrace($i, $backtrace) . "\n";
298            }
299            $msg .= "Full server log:\n";
300            global $execution_stats;
301            $msg .= $execution_stats['log_copy'];
302            if (function_exists("html_escape")) {
303                print("<pre>".html_escape($msg)."</pre>");
304            } else {
305                print("<pre>".htmlentities($msg)."</pre>");
306            }
307        }
308        if (IA_HTTP_ENV && !IA_DEVELOPMENT_MODE) {
309            header('HTTP/1.0 500 Internal Error');
310        }
311        die();
312    }
313}
314
315// Put execution stats into log
316function log_execution_stats() {
317    global $execution_stats;
318    log_assert($execution_stats);
319    log_assert(IA_DEVELOPMENT_MODE);
320
321    $msg = 'Time='
322           .(round((microtime(true)-$execution_stats['timestamp'])*100)/100)
323           .'s';
324    $msg .= '; Queries='.$execution_stats['queries'];
325    $msg .= '; Memory='.number_format(memory_get_usage()/1024/1024, 2).'MB';
326    $msg .= '; Memory='.memory_get_usage().'bytes';
327
328    log_print("Execution stats: ".$msg);
329}
330
331// Change the default error handler.
332set_error_handler("logging_error_handler");
333
334?>
Note: See TracBrowser for help on using the repository browser.