本文目录导读:

在PHP项目中进行数据订正时,良好的日志记录至关重要,它能帮助你追溯问题、审计操作和排查错误,以下是一个系统化的方案:
核心日志记录策略
基础日志记录
class DataCorrectionLogger {
private $logFile;
private $correctionId;
public function __construct($logFile = 'corrections.log') {
$this->logFile = $logFile;
$this->correctionId = uniqid('CORR_', true);
}
public function logCorrection($table, $recordId, $oldData, $newData, $operator, $reason) {
$logEntry = [
'correction_id' => $this->correctionId,
'timestamp' => date('Y-m-d H:i:s'),
'table' => $table,
'record_id' => $recordId,
'operator' => $operator,
'reason' => $reason,
'before' => $oldData,
'after' => $newData,
'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'CLI',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'CLI'
];
$this->writeLog($logEntry);
}
private function writeLog($entry) {
$line = json_encode($entry) . PHP_EOL;
file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX);
}
}
数据库审计表设计
创建专门的审计表
CREATE TABLE data_correction_audit (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
correction_id VARCHAR(64) NOT NULL,
table_name VARCHAR(100) NOT NULL,
record_id BIGINT NOT NULL,
operation_type ENUM('UPDATE', 'DELETE', 'INSERT') NOT NULL,
old_data JSON,
new_data JSON,
operator_id INT,
operator_name VARCHAR(100),
operator_ip VARCHAR(45),
reason TEXT,
status ENUM('PENDING', 'COMPLETED', 'ROLLED_BACK') DEFAULT 'COMPLETED',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
rollback_at TIMESTAMP NULL,
INDEX idx_correction_id (correction_id),
INDEX idx_table_record (table_name, record_id),
INDEX idx_operator (operator_id),
INDEX idx_created_at (created_at)
);
统一的数据订正类
class DataCorrectionManager {
private $db;
private $logger;
private $auditTable = 'data_correction_audit';
public function __construct($dbConnection) {
$this->db = $dbConnection;
$this->logger = new DataCorrectionLogger();
}
/**
* 执行数据订正
*/
public function correctData($table, $recordId, array $changes, $operatorId, $operatorName, $reason) {
try {
// 1. 获取原始数据
$oldData = $this->getRecord($table, $recordId);
if (!$oldData) {
throw new Exception("Record not found: {$table}.{$recordId}");
}
// 2. 开启事务
$this->db->beginTransaction();
// 3. 更新数据
$updateResult = $this->applyCorrection($table, $recordId, $changes);
// 4. 获取更新后数据
$newData = $this->getRecord($table, $recordId);
// 5. 写入审计日志
$auditId = $this->saveAuditLog(
$table, $recordId, 'UPDATE',
$oldData, $newData,
$operatorId, $operatorName,
$reason
);
// 6. 提交事务
$this->db->commit();
// 7. 记录业务日志
$this->logger->logCorrection(
$table, $recordId, $oldData, $newData,
$operatorName, $reason
);
return [
'success' => true,
'audit_id' => $auditId,
'correction_id' => $this->logger->correctionId
];
} catch (Exception $e) {
$this->db->rollBack();
$this->logError($e, $table, $recordId, $operatorName);
throw $e;
}
}
private function saveAuditLog($table, $recordId, $operation, $oldData, $newData,
$operatorId, $operatorName, $reason) {
$sql = "INSERT INTO {$this->auditTable}
(correction_id, table_name, record_id, operation_type,
old_data, new_data, operator_id, operator_name,
operator_ip, reason)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
$stmt = $this->db->prepare($sql);
$stmt->execute([
$this->logger->correctionId,
$table,
$recordId,
$operation,
json_encode($oldData),
json_encode($newData),
$operatorId,
$operatorName,
$_SERVER['REMOTE_ADDR'] ?? '127.0.0.1',
$reason
]);
return $this->db->lastInsertId();
}
}
批量订正与文件日志
批量操作日志
class BatchCorrectionLogger {
private $batchId;
private $csvLog;
public function __construct() {
$this->batchId = date('Ymd_His') . '_' . uniqid();
$this->csvLog = fopen("corrections/{$this->batchId}.csv", 'w');
// CSV头
fputcsv($this->csvLog, [
'时间', '操作类型', '表名', '记录ID',
'操作前', '操作后', '操作人', '原因', '状态'
]);
}
public function logSingle($type, $table, $id, $before, $after, $operator, $reason, $status) {
fputcsv($this->csvLog, [
date('Y-m-d H:i:s'),
$type,
$table,
$id,
json_encode($before),
json_encode($after),
$operator,
$reason,
$status
]);
}
public function close() {
fclose($this->csvLog);
}
}
追踪查询与回溯
查询日志系统
class CorrectionQuery {
private $db;
public function getCorrectionsByDate($startDate, $endDate) {
$sql = "SELECT * FROM data_correction_audit
WHERE created_at BETWEEN ? AND ?
ORDER BY created_at DESC";
$stmt = $this->db->prepare($sql);
$stmt->execute([$startDate, $endDate]);
return $stmt->fetchAll();
}
public function getRecordHistory($table, $recordId) {
$sql = "SELECT * FROM data_correction_audit
WHERE table_name = ? AND record_id = ?
ORDER BY created_at ASC";
$stmt = $this->db->prepare($sql);
$stmt->execute([$table, $recordId]);
return $this->buildTimeline($stmt->fetchAll());
}
private function buildTimeline($records) {
$timeline = [];
foreach ($records as $record) {
$timeline[] = [
'time' => $record['created_at'],
'operator' => $record['operator_name'],
'reason' => $record['reason'],
'changes' => $this->calculateDiff(
json_decode($record['old_data'], true),
json_decode($record['new_data'], true)
)
];
}
return $timeline;
}
private function calculateDiff($old, $new) {
$diff = [];
foreach ($new as $key => $value) {
if ($old[$key] !== $value) {
$diff[$key] = [
'from' => $old[$key],
'to' => $value
];
}
}
return $diff;
}
}
最佳实践
日志级别与内容
/**
* 使用多种日志级别
*/
class CorrectionLogger {
const LEVEL_INFO = 'INFO';
const LEVEL_WARNING = 'WARNING';
const LEVEL_ERROR = 'ERROR';
public function log($level, $message, array $context = []) {
$logEntry = [
'level' => $level,
'message' => $message,
'context' => $context,
'stack_trace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5),
'memory_usage' => memory_get_usage(true)
];
// 根据级别决定存储位置
switch ($level) {
case self::LEVEL_ERROR:
$this->storeToDatabase($logEntry);
$this->sendAlert($logEntry);
break;
default:
$this->storeToFile($logEntry);
}
}
}
数据回滚支持
class RollbackManager {
public function rollbackCorrection($correctionId) {
// 1. 获取审计记录
$record = $this->getAuditRecord($correctionId);
// 2. 验证是否可回滚
if ($record['status'] !== 'COMPLETED') {
throw new Exception('Cannot rollback: status is ' . $record['status']);
}
// 3. 执行回滚
$this->db->beginTransaction();
try {
$oldData = json_decode($record['old_data'], true);
$this->updateRecord($record['table_name'], $record['record_id'], $oldData);
// 4. 更新审计状态
$this->updateAuditStatus($correctionId, 'ROLLED_BACK');
$this->db->commit();
// 5. 记录回滚日志
$this->logRollback($correctionId, $oldData);
} catch (Exception $e) {
$this->db->rollBack();
throw $e;
}
}
}
实际使用示例
// 在业务代码中使用
try {
$correction = new DataCorrectionManager($db);
$result = $correction->correctData(
'users',
123,
['email' => 'new@example.com', 'phone' => '13800138000'],
1,
'admin',
'用户请求更新联系方式'
);
if ($result['success']) {
echo "订正成功,审计ID: " . $result['audit_id'];
}
} catch (Exception $e) {
// 记录错误日志
error_log("数据订正失败: " . $e->getMessage());
// 发送告警
$alertService->send("数据订正失败: {$table}.{$recordId}");
}
建议的日志系统架构:
- 数据库审计表 - 核心记录,支持查询和回滚
- 文件日志 - 详细操作日志,便于排查
- 监控告警 - 异常操作实时通知
- 定期清理 - 设置日志保留期限
- 权限控制 - 只有授权用户可查看日志
这样的设计既能满足追溯需求,又能保证系统的安全和可维护性。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播