~eventum-developers/eventum/trunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 encoding=utf-8: */
// +----------------------------------------------------------------------+
// | Eventum - Issue Tracking System                                      |
// +----------------------------------------------------------------------+
// | Copyright (c) 2003 - 2008 MySQL AB                                   |
// | Copyright (c) 2008 - 2009 Sun Microsystem Inc.                       |
// |                                                                      |
// | This program is free software; you can redistribute it and/or modify |
// | it under the terms of the GNU General Public License as published by |
// | the Free Software Foundation; either version 2 of the License, or    |
// | (at your option) any later version.                                  |
// |                                                                      |
// | This program is distributed in the hope that it will be useful,      |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        |
// | GNU General Public License for more details.                         |
// |                                                                      |
// | You should have received a copy of the GNU General Public License    |
// | along with this program; if not, write to:                           |
// |                                                                      |
// | Free Software Foundation, Inc.                                       |
// | 59 Temple Place - Suite 330                                          |
// | Boston, MA 02111-1307, USA.                                          |
// +----------------------------------------------------------------------+
// | Authors: João Prado Maia <jpm@mysql.com>                             |
// +----------------------------------------------------------------------+
//
// @(#) $Id: class.error_handler.php 3868 2009-03-30 00:22:35Z glen $
//


/**
 * Class to manage all tasks related to error conditions of the site, such as
 * logging facilities or alert notifications to the site administrators.
 *
 * @version 1.0
 * @author João Prado Maia <jpm@mysql.com>
 * @author Elan Ruusamäe <glen@delfi.ee>
 */

class Error_Handler
{
    /**
     * Logs the error condition to a specific file and if asked and possible
     * queue error in mail queue for reporting.
     *
     * @access public
     * @param  mixed $error_msg The error message
     * @param  string $script The script name where the error happened
     * @param  integer $line The line number where the error happened
     * @param  boolean $notify_error Whether error should be notified by email.
     */
    static public function logError($error_msg = 'unknown', $script = 'unknown', $line = 'unknown', $notify_error = true)
    {
        $msg =& self::_createErrorReport($error_msg, $script, $line);

        file_put_contents(APP_ERROR_LOG, array(date('[D M d H:i:s Y] '), $msg), FILE_APPEND);

        // if there's no database connection, then we cannot possibly queue up the error emails
        if ($notify_error === false || is_null(DB_Helper::getInstance())) {
            return;
        }

        $setup = Setup::load();
        if (@$setup['email_error']['status'] == 'enabled') {
            $notify_list = trim($setup['email_error']['addresses']);
            if (empty($notify_list)) {
                return false;
            }
            self::_notify($msg, $setup['smtp']['from'], $notify_list, $script, $line);
        }
    }

    /**
     * Notifies site administrators of the error condition
     *
     * @access private
     * @param  string $notify_msg The formatted error message
     * @param  string $notify_from Sender of the email
     * @param  string $notify_list Email addresses to whom send the error report.
     */
    static private function _notify(&$notify_msg, $notify_from, $notify_list, $script, $line)
    {
        $backtrace = debug_backtrace();
        array_splice($backtrace, 0, 2);
        for ($i = 0; $i < count($backtrace); $i++) {
            // avoid recursion?
            if ($backtrace[$i]['class'] == __CLASS__) {
                return;
            }
        }

        $time = time();
        $date = date('m/d/Y H:i:s', $time);
        $msg = "Hello,\n\n";
        $msg .= $notify_msg;

        // this checks that we're not running from commandline (cron for example)
        if (isset($_SERVER['REMOTE_ADDR'])) {
            $msg .= "That happened on page '" . $_SERVER['SCRIPT_NAME'] . "' from IP Address '" . $_SERVER['REMOTE_ADDR'];
            if (!empty($_SERVER['HTTP_REFERER'])) {
                $msg  .= "' coming from the page (referrer) '" . $_SERVER['HTTP_REFERER'] . "'";
            }
            $msg .= ".\n\nThe user agent given was '" . $_SERVER['HTTP_USER_AGENT'] . "'.\n\n";
        }
        $msg .= "-- \nSincerely yours,\nAutomated Error_Handler Class";

        // query database for 'max_allowed_packet'
        $stmt = "show variables like 'max_allowed_packet'";
        $res =& DB_Helper::getInstance()->query($stmt);
        if (PEAR::isError($res)) {
            // we failed, assume 8M
            $max_allowed_packet = 8387584;
        } else {
            $arr = $res->fetchRow(DB_FETCHMODE_ORDERED);
            $max_allowed_packet = $arr[1];
            $res->free();
        }

        // skip error details of an email notification about a query that
        // was bigger than max_allowed_packet + 1024
        if (strlen($msg) > $max_allowed_packet + 1024) {
            return false;
        }

        $notify_list = str_replace(';', ',', $notify_list);
        $notify_list = explode(',', $notify_list);
        $subject = APP_SITE_NAME . ' - Error found! - ' . $date;

        foreach ($notify_list as $notify_email) {
            $mail = new Mail_Helper;
            $mail->setTextBody($msg);
            $mail->send($notify_from, $notify_email, $subject, 0, false, 'error');
        }
    }

    /**
     * Formats backtrace
     *
     * @access public
     * @param  array    $backtrace The backtrace to format
     * @return string   A nicely formatted backtrace.
     */
    static private function format_backtrace($backtrace = null)
    {
        if ($backtrace == null) {
            $backtrace = debug_backtrace();
        }

        // we process backtrace to truncate large blobs
        $cutoff = 1024;

        $msg = '';
        foreach ($backtrace as $e) {
            // backtrace frame contains: [file] [line] [function] [class] [type] [args]
            $f = $e['file'];
            $f = str_replace(APP_INC_PATH, 'APP_INC_PATH', $f);
            $f = str_replace(APP_PATH, 'APP_PATH', $f);

            $fn = $e['function'];
            if (isset($e['class'])) {
                $fn = $e['class']. $e['type']. $fn;
            }
            $a = '';
            if ($e['args']) {
                $z = array();
                foreach ($e['args'] as $x) {
                    if (is_string($x)) {
                        if (strlen($x) > $cutoff) {
                            $z[] = sprintf("(string )'%.{$cutoff}s'...", $x);
                        } else {
                            $z[] = sprintf("(string )'%s'", $x);
                        }
                    } elseif (is_object($x)) {
                        $z[] = 'Object '. get_class($x);

                    } elseif (is_bool($x)) {
                        $z[] = '(bool ) '.$x ? 'true' : 'false';

                    } else {
                        $z[] = '(' . gettype($x). ' )' . $x;
                    }
                }
                $a = join(', ', $z);
            }
            $msg .= sprintf("%s:%d\n  %s(%s)\n", $f, $e['line'], $fn, $a);
        }
        return $msg;
    }

    /**
     * Creates error report.
     *
     * @access private
     * @param  mixed $error_msg The error message
     * @param  string $script The script name where the error happened
     * @param  integer $line The line number where the error happened
     */
    static private function &_createErrorReport(&$error_msg, $script, $line)
    {
        $msg = "An error was found on line '" . $line . "' of script " . "'$script'.\n\n";

        $msg .= "The error message passed to us was:\n\n";
        if ((is_array($error_msg)) && (count($error_msg) > 1)) {
            $msg .= "'" . $error_msg[0] . "'\n\n";
            $msg .= "A more detailed error message follows:\n\n";
            $error_msg = $error_msg[1];
        }

        if (strlen($error_msg) > 1024) {
            $msg .= "'" . substr($error_msg, 0, 1024) . "' ...";

            // try to find native code from DB error
            // [nativecode=1153 ** Got a packet bigger than 'max_allowed_packet' bytes]'
            $nativecode = strstr($error_msg, '[nativecode=');
            if ($nativecode) {
                $msg .= ' ' . $nativecode;
            }
            $msg .= "\n";
        } else {
            $msg .= "'" . $error_msg . "'\n";
        }

        $msg .= "\nA backtrace is available:\n\n";
        $backtrace = debug_backtrace();

        // remove the two entries related to the error handling stuff itself
        array_splice($backtrace, 0, 2);

        $msg .= self::format_backtrace($backtrace);
        $msg .= "\n\n";

        return $msg;
    }
}