客戶回報錯誤但無法提供錯誤訊息時,在資料庫查詢類別的錯誤處理處加入日誌記錄,事後可直接檢視日誌檔案定位 SQL 錯誤。
<?php
/**
* 追加寫入日誌檔案
* @param string $fileName 日誌路徑
* @param string $message 要記錄的訊息
*/
function putFile($fileName, $message) {
// a+ 模式:讀寫、指標在末尾、不存在則建立
if ($fp = fopen($fileName, 'a+')) {
// 格式:訊息 + 時間戳 + 換行
$logLine = $message . date('Y-m-d H:i:s') . "\n\r";
fwrite($fp, $logLine);
fclose($fp);
}
}
// 使用範例
$errorSql = 'SELECT * FROM users WHERE id = \' OR 1=1 --';
putFile('logfile.txt', '[SQL ERROR] ' . $errorSql);
?>
<?php
class Database {
private $pdo;
private $logFile = __DIR__ . '/logs/sql_errors.log';
public function __construct($dsn, $user, $pass) {
$this->pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
}
public function query($sql, $params = []) {
try {
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
return $stmt;
} catch (PDOException $e) {
$this->logError($sql, $params, $e);
throw $e; // 重新拋出讓上層處理
}
}
private function logError($sql, $params, $e) {
$msg = sprintf(
"[%s] SQL: %s | Params: %s | Error: %s\n",
date('Y-m-d H:i:s'),
$sql,
json_encode($params, JSON_UNESCAPED_UNICODE),
$e->getMessage()
);
// 使用 file_put_contents 簡化 (PHP 5+)
file_put_contents($this->logFile, $msg, FILE_APPEND | LOCK_EX);
}
}
?>
LOCK_EX 確保多程序併發寫入不衝突。<?php
function logErrorJson($sql, $params, $errorMsg) {
$entry = [
'timestamp' => date('c'),
'level' => 'ERROR',
'sql' => $sql,
'params' => $params,
'error' => $errorMsg,
'trace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5)
];
$line = json_encode($entry, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n";
file_put_contents('sql_errors.json.log', $line, FILE_APPEND | LOCK_EX);
}
?>
輸出範例:
{"timestamp":"2024-05-20T14:30:00+08:00","level":"ERROR","sql":"SELECT * FROM users WHERE email = ?","params":["test@example.com"],"error":"SQLSTATE[42S02]: Base table or view not found: 1146 Table 'db.users' doesn't exist","trace":[...]}
<?php
// 測試寫入
putFile('test.log', 'Test message ');
echo 'Done. Check test.log content:';
echo '<pre>'; readfile('test.log'); echo '</pre>';
?>