From a1bd9cc2bcebc6db492d5e34de494fc5243e6ce7 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 30 Apr 2023 23:56:54 +0900 Subject: [PATCH] Only record the same error or query once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PHP 8.2로 올라오면서 동일한 오류가 수백~수천 번 반복되어 디버그 데이터가 너무 많은 메모리를 차지하는 문제가 종종 발생하고 있음. 동일한 위치에서 발생한 동일한 오류나 쿼리는 한 번만 기록하고 count를 증가시키도록 하여, 불필요한 메모리 사용을 줄이고 반복되는 오류나 쿼리를 쉽게 찾을 수 있도록 개선함. --- common/framework/Debug.php | 79 +++++++++++++++++++++++++++++------ common/js/debug.js | 15 +++++-- common/tpl/debug_comment.html | 22 ++++++++-- 3 files changed, 97 insertions(+), 19 deletions(-) diff --git a/common/framework/Debug.php b/common/framework/Debug.php index e7bc81dc3..fabbe62a9 100644 --- a/common/framework/Debug.php +++ b/common/framework/Debug.php @@ -53,7 +53,7 @@ class Debug */ public static function getEntries() { - return self::$_entries; + return array_values(self::$_entries); } /** @@ -73,7 +73,7 @@ class Debug */ public static function getErrors() { - return self::$_errors; + return array_values(self::$_errors); } /** @@ -93,7 +93,7 @@ class Debug */ public static function getQueries() { - return self::$_queries; + return array_values(self::$_queries); } /** @@ -281,10 +281,21 @@ class Debug 'file' => isset($backtrace[0]['file']) ? $backtrace[0]['file'] : null, 'line' => isset($backtrace[0]['line']) ? $backtrace[0]['line'] : 0, 'backtrace' => $backtrace, + 'count' => 1, 'time' => microtime(true), 'type' => 'Debug', ); - self::$_entries[] = $entry; + + // Only add the same entry once. + $key = hash_hmac('sha1', serialize([$entry->message, $entry->file, $entry->line]), config('crypto.authentication_key')); + if (isset(self::$_entries[$key])) + { + self::$_entries[$key]->count++; + } + else + { + self::$_entries[$key] = $entry; + } // Add the entry to the error log. if (isset(self::$_config['write_error_log']) && self::$_config['write_error_log'] === 'all') @@ -332,15 +343,27 @@ class Debug $backtrace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS); // Prepare the error entry. - self::$_errors[] = $errinfo = (object)array( + $errinfo = (object)array( 'message' => $message, 'file' => $errfile, 'line' => $errline, 'backtrace' => $backtrace, + 'count' => 1, 'time' => microtime(true), 'type' => self::getErrorType($errno), ); + // If the same error is repeated, only increment the counter. + $key = hash_hmac('sha1', serialize([$errinfo->message, $errinfo->file, $errinfo->line]), config('crypto.authentication_key')); + if (isset(self::$_errors[$key])) + { + self::$_errors[$key]->count++; + } + else + { + self::$_errors[$key] = $errinfo; + } + // Add the entry to the error log. if (isset(self::$_config['write_error_log']) && self::$_config['write_error_log'] === 'all') { @@ -374,14 +397,36 @@ class Debug 'line' => $query['called_line'], 'method' => $query['called_method'], 'backtrace' => $query['backtrace'] ?: array(), + 'count' => 1, 'time' => microtime(true), 'type' => 'Query', ); - self::$_queries[] = $query_object; + // Generate a unique key for this query. + $key = hash_hmac('sha1', serialize([ + $query_object->query_id, + $query_object->query_string, + $query_object->query_connection, + $query_object->file, + $query_object->line, + $query_object->method, + ]), config('crypto.authentication_key')); + + // If the same query is repeated, only increment the counter. + if (isset(self::$_queries[$key])) + { + self::$_queries[$key]->query_time += $query_object->query_time; + self::$_queries[$key]->count++; + } + else + { + self::$_queries[$key] = $query_object; + } + + // Record query time. self::$_query_time += $query_object->query_time; - // Add the entry to the error log if the result wasn't successful. + // Add the query to the error log if the result wasn't successful. if ($query['result'] === 'error') { $error_object = (object)array( @@ -389,11 +434,21 @@ class Debug 'file' => $query_object->file, 'line' => $query_object->line, 'backtrace' => $query_object->backtrace ?: array(), + 'count' => 1, 'time' => $query_object->time, 'type' => 'Query Error', ); - self::$_errors[] = $error_object; + // If the same query error is repeated, only increment the counter. + $key = hash_hmac('sha1', serialize(['QUERY ERROR', $error_object->message, $error_object->file, $error_object->line]), config('crypto.authentication_key')); + if (isset(self::$_errors[$key])) + { + self::$_errors[$key]->count++; + } + else + { + self::$_errors[$key] = $error_object; + } if (self::$_config['write_error_log'] === 'all') { @@ -402,7 +457,7 @@ class Debug } } - // Add the entry to the slow query log. + // Add the query to the slow query log. if ($query_object->query_time && $query_object->query_time >= (self::$_config['log_slow_queries'] ?? 1)) { self::$_slow_queries[] = $query_object; @@ -794,9 +849,9 @@ class Debug 'template' => sprintf('%0.4f sec (count: %d)', $GLOBALS['__template_elapsed__'] ?? 0, $GLOBALS['__TemplateHandlerCalled__'] ?? 0), 'trans' => sprintf('%0.4f sec', $GLOBALS['__trans_content_elapsed__'] ?? 0), ), - 'entries' => self::$_entries, - 'errors' => self::$_errors, - 'queries' => self::$_queries, + 'entries' => array_values(self::$_entries), + 'errors' => array_values(self::$_errors), + 'queries' => array_values(self::$_queries), 'slow_queries' => self::$_slow_queries, 'slow_triggers' => self::$_slow_triggers, 'slow_widgets' => self::$_slow_widgets, diff --git a/common/js/debug.js b/common/js/debug.js index 5bcb03e73..65325627d 100644 --- a/common/js/debug.js +++ b/common/js/debug.js @@ -98,7 +98,10 @@ $(function() { backtrace = $('').appendTo(entry); for (j in data.entries[i].backtrace) { if (data.entries[i].backtrace[j].file) { - backtrace.append($('
  • ').text(data.entries[i].backtrace[j].file + ":" + data.entries[i].backtrace[j].line)); + backtrace.append($('
  • ').text( + data.entries[i].backtrace[j].file + ":" + data.entries[i].backtrace[j].line + + (j == 0 ? (" (×" + data.entries[i].count + ")") : "") + )); } } } @@ -114,7 +117,10 @@ $(function() { backtrace = $('').appendTo(entry); for (j in data.errors[i].backtrace) { if (data.errors[i].backtrace[j].file) { - backtrace.append($('
  • ').text(data.errors[i].backtrace[j].file + ":" + data.errors[i].backtrace[j].line)); + backtrace.append($('
  • ').text( + data.errors[i].backtrace[j].file + ":" + data.errors[i].backtrace[j].line + + (j == 0 ? (" (×" + data.errors[i].count + ")") : "") + )); } } } @@ -129,7 +135,10 @@ $(function() { entry.text(num + ". " + data.queries[i].query_string); description = $('').appendTo(entry); if (data.queries[i].file && data.queries[i].line) { - description.append($('
  • ').text("Caller: " + data.queries[i].file + ":" + data.queries[i].line).append("
    (" + data.queries[i].method + ")")); + description.append($('
  • ').text("Caller: " + + data.queries[i].file + ":" + data.queries[i].line + + (" (×" + data.queries[i].count + ")") + ).append("
    (" + data.queries[i].method + ")")); description.append($('
  • ').text("Connection: " + data.queries[i].query_connection)); description.append($('
  • ').text("Query ID: " + data.queries[i].query_id)); description.append($('
  • ').text("Query Time: " + (data.queries[i].query_time ? (data.queries[i].query_time.toFixed(4) + " sec") : ""))); diff --git a/common/tpl/debug_comment.html b/common/tpl/debug_comment.html index c430027e3..2615e793c 100644 --- a/common/tpl/debug_comment.html +++ b/common/tpl/debug_comment.html @@ -51,11 +51,18 @@ Debug Entries $entry->message = trim(preg_replace('/\r?\n/', "\n" . ' ', print_r($entry->message, true))); } echo sprintf('%02d. %s', ++$entry_count, $entry->message) . "\n"; + $is_first_entry = true; foreach ($entry->backtrace as $key => $backtrace) { if (isset($backtrace['file']) && isset($backtrace['line'])) { - echo sprintf(' - %s line %d', $backtrace['file'], $backtrace['line']) . "\n"; + echo sprintf(' - %s line %d', $backtrace['file'], $backtrace['line']); + if ($is_first_entry) + { + echo ' (×' . $entry->count . ')'; + $is_first_entry = false; + } + echo "\n"; } } } @@ -74,11 +81,18 @@ PHP Errors and Warnings foreach ($data->errors as $error) { echo sprintf('%02d. %s: %s', ++$error_count, $error->type, $error->message) . "\n"; + $is_first_error = true; foreach ($error->backtrace as $key => $backtrace) { if (isset($backtrace['file']) && isset($backtrace['line'])) { - echo sprintf(' - %s line %d', $backtrace['file'], $backtrace['line']) . "\n"; + echo sprintf(' - %s line %d', $backtrace['file'], $backtrace['line']); + if ($is_first_error) + { + echo ' (×' . $error->count . ')'; + $is_first_error = false; + } + echo "\n"; } } } @@ -101,7 +115,7 @@ Database Queries echo sprintf('%02d. %s', ++$query_count, $query->query_string) . "\n"; if (empty($query->backtrace)) { - echo sprintf(' - Caller: %s', $query_caller) . "\n"; + echo sprintf(' - Caller: %s', $query_caller) . ' (×' . $query->count . ')' . "\n"; } echo sprintf(' - Connection: %s', $query->query_connection) . "\n"; echo sprintf(' - Query ID: %s', $query->query_id) . "\n"; @@ -109,7 +123,7 @@ Database Queries echo sprintf(' - Result: %s', $query_result) . "\n"; if (!empty($query->backtrace)) { - echo sprintf(' - Call Stack: %s', $query_caller) . "\n"; + echo sprintf(' - Call Stack: %s', $query_caller) . ' (×' . $query->count . ')' . "\n"; foreach ($query->backtrace ?? [] as $key => $backtrace) { if (isset($backtrace['file']) && isset($backtrace['line']))