mirror of
https://github.com/torrentpier/torrentpier
synced 2025-08-22 14:23:57 -07:00
refactor(database): enhance error logging and handling in Database and DatabaseDebugger classes
- Updated error handling in Database class to provide more meaningful error messages, including detailed PDO error information. - Enhanced log_error method in DatabaseDebugger to accept exceptions, allowing for more reliable error logging with comprehensive context. - Improved user-facing error messages while maintaining detailed logging for administrators and developers. - Added checks for connection status and query context in error logs to aid in debugging.
This commit is contained in:
parent
7e723d6ad8
commit
8d4723f64c
3 changed files with 192 additions and 11 deletions
|
@ -26,9 +26,9 @@ $posts_without_attach = $topics_without_attach = [];
|
||||||
|
|
||||||
DB()->query("
|
DB()->query("
|
||||||
CREATE TEMPORARY TABLE $tmp_attach_tbl (
|
CREATE TEMPORARY TABLE $tmp_attach_tbl (
|
||||||
physical_filename VARCHAR(255) NOT NULL default '',
|
physical_filename VARCHAR(255) NOT NULL default '' COLLATE utf8mb4_unicode_ci,
|
||||||
KEY physical_filename (physical_filename(20))
|
KEY physical_filename (physical_filename(20))
|
||||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4
|
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci
|
||||||
");
|
");
|
||||||
DB()->add_shutdown_query("DROP TEMPORARY TABLE IF EXISTS $tmp_attach_tbl");
|
DB()->add_shutdown_query("DROP TEMPORARY TABLE IF EXISTS $tmp_attach_tbl");
|
||||||
|
|
||||||
|
|
|
@ -188,7 +188,7 @@ class Database
|
||||||
// Initialize affected rows to 0 (most queries don't affect rows)
|
// Initialize affected rows to 0 (most queries don't affect rows)
|
||||||
$this->last_affected_rows = 0;
|
$this->last_affected_rows = 0;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->debugger->log_error();
|
$this->debugger->log_error($e);
|
||||||
$this->result = null;
|
$this->result = null;
|
||||||
$this->last_affected_rows = 0;
|
$this->last_affected_rows = 0;
|
||||||
}
|
}
|
||||||
|
@ -549,9 +549,28 @@ class Database
|
||||||
if ($this->connection) {
|
if ($this->connection) {
|
||||||
try {
|
try {
|
||||||
$pdo = $this->connection->getPdo();
|
$pdo = $this->connection->getPdo();
|
||||||
|
$errorCode = $pdo->errorCode();
|
||||||
|
$errorInfo = $pdo->errorInfo();
|
||||||
|
|
||||||
|
// Filter out "no error" states - PDO returns '00000' when there's no error
|
||||||
|
if (!$errorCode || $errorCode === '00000') {
|
||||||
|
return ['code' => '', 'message' => ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build meaningful error message from errorInfo array
|
||||||
|
// errorInfo format: [SQLSTATE, driver-specific error code, driver-specific error message]
|
||||||
|
$message = '';
|
||||||
|
if (isset($errorInfo[2]) && $errorInfo[2]) {
|
||||||
|
$message = $errorInfo[2]; // Driver-specific error message is most informative
|
||||||
|
} elseif (isset($errorInfo[1]) && $errorInfo[1]) {
|
||||||
|
$message = "Error code: " . $errorInfo[1];
|
||||||
|
} else {
|
||||||
|
$message = "SQLSTATE: " . $errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'code' => $pdo->errorCode(),
|
'code' => $errorCode,
|
||||||
'message' => implode(': ', $pdo->errorInfo())
|
'message' => $message
|
||||||
];
|
];
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return ['code' => $e->getCode(), 'message' => $e->getMessage()];
|
return ['code' => $e->getCode(), 'message' => $e->getMessage()];
|
||||||
|
@ -753,7 +772,93 @@ class Database
|
||||||
public function trigger_error(string $msg = 'Database Error'): void
|
public function trigger_error(string $msg = 'Database Error'): void
|
||||||
{
|
{
|
||||||
$error = $this->sql_error();
|
$error = $this->sql_error();
|
||||||
$error_msg = "$msg: " . $error['message'];
|
|
||||||
|
// Build a meaningful error message
|
||||||
|
if (!empty($error['message'])) {
|
||||||
|
$error_msg = "$msg: " . $error['message'];
|
||||||
|
if (!empty($error['code'])) {
|
||||||
|
$error_msg = "$msg ({$error['code']}): " . $error['message'];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Base error message for all users
|
||||||
|
$error_msg = "$msg: Database operation failed";
|
||||||
|
|
||||||
|
// Only add detailed debugging information for administrators or in development mode
|
||||||
|
$is_admin = defined('IS_ADMIN') && IS_ADMIN;
|
||||||
|
$is_dev_mode = (defined('APP_ENV') && APP_ENV === 'local') || (defined('DBG_USER') && DBG_USER) || function_exists('dev');
|
||||||
|
|
||||||
|
if ($is_admin || $is_dev_mode) {
|
||||||
|
// Gather detailed debugging information - ONLY for admins/developers
|
||||||
|
$debug_info = [];
|
||||||
|
|
||||||
|
// Connection status
|
||||||
|
if ($this->connection) {
|
||||||
|
$debug_info[] = "Connection: Active";
|
||||||
|
try {
|
||||||
|
$pdo = $this->connection->getPdo();
|
||||||
|
if ($pdo) {
|
||||||
|
$debug_info[] = "PDO: Available";
|
||||||
|
$errorInfo = $pdo->errorInfo();
|
||||||
|
if ($errorInfo && count($errorInfo) >= 3) {
|
||||||
|
$debug_info[] = "PDO ErrorInfo: " . json_encode($errorInfo);
|
||||||
|
}
|
||||||
|
$debug_info[] = "PDO ErrorCode: " . $pdo->errorCode();
|
||||||
|
} else {
|
||||||
|
$debug_info[] = "PDO: Null";
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$debug_info[] = "PDO Check Failed: " . $e->getMessage();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$debug_info[] = "Connection: None";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query information
|
||||||
|
if ($this->cur_query) {
|
||||||
|
$debug_info[] = "Last Query: " . substr($this->cur_query, 0, 200) . (strlen($this->cur_query) > 200 ? '...' : '');
|
||||||
|
} else {
|
||||||
|
$debug_info[] = "Last Query: None";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Database information
|
||||||
|
$debug_info[] = "Database: " . ($this->selected_db ?: 'None');
|
||||||
|
$debug_info[] = "Server: " . $this->db_server;
|
||||||
|
|
||||||
|
// Recent queries from debug log (if available)
|
||||||
|
if (isset($this->debugger->dbg) && !empty($this->debugger->dbg)) {
|
||||||
|
$recent_queries = array_slice($this->debugger->dbg, -3); // Last 3 queries
|
||||||
|
$debug_info[] = "Recent Queries Count: " . count($recent_queries);
|
||||||
|
foreach ($recent_queries as $i => $query_info) {
|
||||||
|
$debug_info[] = "Query " . ($i + 1) . ": " . substr($query_info['sql'] ?? 'Unknown', 0, 100) . (strlen($query_info['sql'] ?? '') > 100 ? '...' : '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($debug_info) {
|
||||||
|
$error_msg .= " [DEBUG: " . implode("; ", $debug_info) . "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log this for investigation
|
||||||
|
if (function_exists('bb_log')) {
|
||||||
|
bb_log("Unknown Database Error Debug:\n" . implode("\n", $debug_info), 'unknown_db_errors');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// For regular users: generic message only + contact admin hint
|
||||||
|
$error_msg = "$msg: A database error occurred. Please contact the administrator if this problem persists.";
|
||||||
|
|
||||||
|
// Still log basic information for debugging
|
||||||
|
if (function_exists('bb_log')) {
|
||||||
|
bb_log("Database Error (User-facing): $error_msg\nRequest: " . ($_SERVER['REQUEST_URI'] ?? 'CLI'), 'user_db_errors');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add query context for debugging (but only for admins/developers)
|
||||||
|
$is_admin = defined('IS_ADMIN') && IS_ADMIN;
|
||||||
|
$is_dev_mode = (defined('APP_ENV') && APP_ENV === 'local') || (defined('DBG_USER') && DBG_USER) || function_exists('dev');
|
||||||
|
|
||||||
|
if ($this->cur_query && ($is_admin || $is_dev_mode)) {
|
||||||
|
$error_msg .= "\nQuery: " . $this->cur_query;
|
||||||
|
}
|
||||||
|
|
||||||
if (function_exists('bb_die')) {
|
if (function_exists('bb_die')) {
|
||||||
bb_die($error_msg);
|
bb_die($error_msg);
|
||||||
|
@ -797,9 +902,9 @@ class Database
|
||||||
/**
|
/**
|
||||||
* Log error (delegated to debugger)
|
* Log error (delegated to debugger)
|
||||||
*/
|
*/
|
||||||
public function log_error(): void
|
public function log_error(?\Exception $exception = null): void
|
||||||
{
|
{
|
||||||
$this->debugger->log_error();
|
$this->debugger->log_error($exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -278,11 +278,87 @@ class DatabaseDebugger
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log error
|
* Log error
|
||||||
|
*
|
||||||
|
* NOTE: This method logs detailed information to FILES only (error_log, bb_log).
|
||||||
|
* Log files are not accessible to regular users, so detailed information is safe here.
|
||||||
|
* User-facing error display is handled separately with proper security checks.
|
||||||
*/
|
*/
|
||||||
public function log_error(): void
|
public function log_error(?\Exception $exception = null): void
|
||||||
{
|
{
|
||||||
$error = $this->db->sql_error();
|
$error_details = [];
|
||||||
error_log("Database Error: " . $error['message'] . " Query: " . $this->db->cur_query);
|
$error_msg = '';
|
||||||
|
|
||||||
|
if ($exception) {
|
||||||
|
// Use the actual exception information which is more reliable
|
||||||
|
$error_msg = "Database Error: " . $exception->getMessage();
|
||||||
|
$error_code = $exception->getCode();
|
||||||
|
if ($error_code) {
|
||||||
|
$error_msg = "Database Error ({$error_code}): " . $exception->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect detailed error information
|
||||||
|
$error_details[] = "Exception: " . get_class($exception);
|
||||||
|
$error_details[] = "Message: " . $exception->getMessage();
|
||||||
|
$error_details[] = "Code: " . $exception->getCode();
|
||||||
|
$error_details[] = "File: " . $exception->getFile() . ":" . $exception->getLine();
|
||||||
|
|
||||||
|
// Add PDO-specific details if it's a PDO exception
|
||||||
|
if ($exception instanceof \PDOException) {
|
||||||
|
$error_details[] = "PDO Error Info: " . json_encode($exception->errorInfo ?? []);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback to PDO error state (legacy behavior)
|
||||||
|
$error = $this->db->sql_error();
|
||||||
|
|
||||||
|
// Only log if there's an actual error (not 00000 which means "no error")
|
||||||
|
if (!$error['code'] || $error['code'] === '00000' || !$error['message']) {
|
||||||
|
return; // Don't log empty or "no error" states
|
||||||
|
}
|
||||||
|
|
||||||
|
$error_msg = "Database Error ({$error['code']}): " . $error['message'];
|
||||||
|
$error_details[] = "PDO Error Code: " . $error['code'];
|
||||||
|
$error_details[] = "PDO Error Message: " . $error['message'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add comprehensive context for debugging
|
||||||
|
$error_details[] = "Query: " . ($this->db->cur_query ?: 'None');
|
||||||
|
$error_details[] = "Source: " . $this->debug_find_source();
|
||||||
|
$error_details[] = "Database: " . ($this->db->selected_db ?: 'None');
|
||||||
|
$error_details[] = "Server: " . $this->db->db_server;
|
||||||
|
$error_details[] = "Timestamp: " . date('Y-m-d H:i:s');
|
||||||
|
$error_details[] = "Request URI: " . ($_SERVER['REQUEST_URI'] ?? 'CLI');
|
||||||
|
$error_details[] = "User IP: " . ($_SERVER['REMOTE_ADDR'] ?? 'Unknown');
|
||||||
|
|
||||||
|
// Check connection status
|
||||||
|
try {
|
||||||
|
if ($this->db->connection) {
|
||||||
|
$error_details[] = "Connection Status: Active";
|
||||||
|
$pdo = $this->db->connection->getPdo();
|
||||||
|
$error_details[] = "PDO Connection: " . ($pdo ? 'Available' : 'Null');
|
||||||
|
if ($pdo) {
|
||||||
|
$errorInfo = $pdo->errorInfo();
|
||||||
|
$error_details[] = "Current PDO Error Info: " . json_encode($errorInfo);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$error_details[] = "Connection Status: No connection";
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$error_details[] = "Connection Check Failed: " . $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build comprehensive log message
|
||||||
|
$log_message = $error_msg . "\n" . implode("\n", $error_details);
|
||||||
|
|
||||||
|
// Log to both error_log and TorrentPier's logging system
|
||||||
|
error_log($error_msg);
|
||||||
|
|
||||||
|
// Use TorrentPier's bb_log for better file management and organization
|
||||||
|
if (function_exists('bb_log')) {
|
||||||
|
bb_log($log_message, 'database_errors');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also log to PHP error log for immediate access
|
||||||
|
error_log("DETAILED: " . $log_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue