Skip to content

16.5 错误处理与日志

错误处理的重要性

错误处理是软件开发中的重要组成部分,它可以:

  • 提高应用程序的稳定性:妥善处理错误可以防止应用程序崩溃
  • 提高用户体验:向用户提供友好的错误信息,而不是技术错误详情
  • 便于调试和排查:记录错误信息,帮助开发人员快速定位和解决问题
  • 提高系统的安全性:避免将敏感的错误信息暴露给用户

PHP 错误处理机制

1. 错误类型

PHP 中的错误类型包括:

  • Notice:通知级别的错误,如使用未定义的变量
  • Warning:警告级别的错误,如函数参数错误
  • Error:错误级别的错误,如语法错误,会导致脚本终止执行
  • Parse Error:解析错误,如语法错误,会导致脚本无法执行
  • Fatal Error:致命错误,如调用不存在的函数,会导致脚本终止执行
  • Deprecated:已弃用的功能警告

2. 错误报告设置

通过 error_reporting 函数或 php.ini 文件设置错误报告级别:

php
// 报告所有错误(除了 E_NOTICE)
error_reporting(E_ALL & ~E_NOTICE);

// 报告所有错误
error_reporting(E_ALL);

// 关闭错误报告
error_reporting(0);

3. 自定义错误处理函数

使用 set_error_handler 函数设置自定义错误处理函数:

php
// 自定义错误处理函数
function custom_error_handler($errno, $errstr, $errfile, $errline) {
    // 记录错误信息
    error_log("Error: $errstr in $errfile on line $errline");
    
    // 向用户显示友好的错误信息
    if (ENVIRONMENT === 'production') {
        echo "抱歉,系统发生错误,请稍后重试";
    } else {
        echo "Error: $errstr in $errfile on line $errline";
    }
    
    return true; // 防止默认错误处理
}

// 设置自定义错误处理函数
set_error_handler('custom_error_handler');

4. 异常处理

PHP 5 引入了异常处理机制,使用 try-catch 块捕获和处理异常:

php
try {
    // 可能抛出异常的代码
    if ($user_id <= 0) {
        throw new Exception('无效的用户 ID');
    }
    
    $user = get_user_by_id($user_id);
} catch (Exception $e) {
    // 处理异常
    error_log('Exception: ' . $e->getMessage() . ' in ' . $e->getFile() . ' on line ' . $e->getLine());
    echo "操作失败:" . $e->getMessage();
}

5. 自定义异常类

创建自定义异常类,以便更好地分类和处理异常:

php
// 自定义异常类
class UserException extends Exception {
    // 自定义异常处理逻辑
}

class DatabaseException extends Exception {
    // 自定义异常处理逻辑
}

// 使用自定义异常
try {
    if (!connect_to_database()) {
        throw new DatabaseException('数据库连接失败');
    }
} catch (DatabaseException $e) {
    error_log('Database Error: ' . $e->getMessage());
    echo "数据库错误,请稍后重试";
} catch (Exception $e) {
    error_log('General Error: ' . $e->getMessage());
    echo "系统错误,请稍后重试";
}

日志记录

1. 使用 error_log 函数

error_log 函数可以将错误信息记录到日志文件、系统日志或发送电子邮件:

php
// 记录到默认错误日志
error_log('This is an error message');

// 记录到指定文件
error_log('This is an error message', 3, 'error.log');

// 发送电子邮件
error_log('This is an error message', 1, 'admin@example.com');

2. 使用 Monolog 库

Monolog 是一个功能强大的日志库,可以灵活地配置日志处理:

安装

bash
composer require monolog/monolog

使用

php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\FirePHPHandler;

// 创建日志记录器
$logger = new Logger('app');

// 添加处理器
$logger->pushHandler(new StreamHandler(__DIR__ . '/logs/app.log', Logger::DEBUG));
$logger->pushHandler(new FirePHPHandler());

// 记录日志
$logger->info('用户登录', ['user_id' => 1, 'ip' => $_SERVER['REMOTE_ADDR']]);
$logger->error('数据库连接失败', ['error' => $e->getMessage()]);
$logger->warning('权限不足', ['user_id' => 1, 'action' => 'delete']);

3. 日志级别

Monolog 支持以下日志级别(从低到高):

  • DEBUG:详细的调试信息
  • INFO:一般信息
  • NOTICE:注意信息
  • WARNING:警告信息
  • ERROR:错误信息
  • CRITICAL:严重错误
  • ALERT:需要立即处理的错误
  • EMERGENCY:系统级紧急错误

4. 日志轮转

为了防止日志文件过大,应该实现日志轮转:

php
use Monolog\Handler\RotatingFileHandler;

// 创建按日期轮转的日志处理器
$logger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/app.log', 30, Logger::DEBUG));

实战演练

场景:用户注册功能的错误处理

不安全的实现

php
<?php
$username = $_POST['username'];
$email = $_POST['email'];
$password = $_POST['password'];

// 连接数据库
$conn = mysqli_connect('localhost', 'root', '', 'test');

// 插入用户数据
$sql = "INSERT INTO users (username, email, password) VALUES ('$username', '$email', '$password')";
$result = mysqli_query($conn, $sql);

if ($result) {
    echo "注册成功";
} else {
    echo "注册失败";
}
?>

安全的实现

php
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

// 创建日志记录器
$logger = new Logger('registration');
$logger->pushHandler(new StreamHandler(__DIR__ . '/logs/registration.log', Logger::DEBUG));

try {
    $username = $_POST['username'];
    $email = $_POST['email'];
    $password = $_POST['password'];
    
    // 验证输入
    if (empty($username)) {
        throw new Exception('用户名不能为空');
    }
    
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        throw new Exception('邮箱格式不正确');
    }
    
    if (strlen($password) < 6) {
        throw new Exception('密码长度至少为 6 位');
    }
    
    // 连接数据库
    $conn = mysqli_connect('localhost', 'root', '', 'test');
    if (!$conn) {
        throw new Exception('数据库连接失败');
    }
    
    // 检查用户名是否已存在
    $stmt = $conn->prepare("SELECT id FROM users WHERE username = ?");
    $stmt->bind_param("s", $username);
    $stmt->execute();
    $result = $stmt->get_result();
    
    if ($result->num_rows > 0) {
        throw new Exception('用户名已存在');
    }
    
    // 插入用户数据
    $stmt = $conn->prepare("INSERT INTO users (username, email, password) VALUES (?, ?, ?)");
    $stmt->bind_param("sss", $username, $email, $password);
    $result = $stmt->execute();
    
    if (!$result) {
        throw new Exception('注册失败,请稍后重试');
    }
    
    $logger->info('用户注册成功', ['username' => $username, 'email' => $email]);
    echo "注册成功";
    
} catch (Exception $e) {
    $logger->error('用户注册失败', ['error' => $e->getMessage(), 'username' => $username, 'email' => $email]);
    echo "注册失败:" . $e->getMessage();
}
?>

错误处理最佳实践

  1. 使用异常处理:优先使用 try-catch 块处理异常,而不是传统的错误处理
  2. 记录错误信息:将错误信息记录到日志文件,便于调试和排查
  3. 向用户显示友好的错误信息:在生产环境中,向用户显示友好的错误信息,而不是技术错误详情
  4. 使用不同的错误处理策略:根据环境(开发/生产)使用不同的错误处理策略
  5. 使用日志级别:根据错误的严重程度使用不同的日志级别
  6. 实现日志轮转:防止日志文件过大,影响系统性能
  7. 定期检查日志:定期检查日志文件,及时发现和解决问题

总结

错误处理和日志记录是 PHP 应用程序开发中的重要组成部分,它们可以提高应用程序的稳定性、安全性和可维护性。通过使用 PHP 的错误处理机制、异常处理、自定义错误处理函数,以及专业的日志库(如 Monolog),可以构建更加健壮、可靠的 PHP 应用程序。

在开发过程中,应该始终重视错误处理和日志记录,将其作为开发流程的一部分,确保应用程序的质量和可靠性。

© 2026 编程马·菜鸟教程 版权所有