本文目录导读:

在PHP项目中实现日志分级,我推荐几种实用方案,从简单到完整逐步介绍。
使用PSR-3日志接口标准(推荐)
PSR-3定义了8个日志级别,是目前PHP生态的标准做法:
<?php
// 使用 monolog/monolog 库(最流行)
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\FirePHPHandler;
// 创建日志通道
$log = new Logger('my_app');
// 添加处理器,设置不同级别
$log->pushHandler(new StreamHandler(__DIR__.'/logs/debug.log', Logger::DEBUG));
$log->pushHandler(new StreamHandler(__DIR__.'/logs/error.log', Logger::ERROR));
// 使用不同级别记录日志
$log->debug('调试信息', ['user_id' => 123]);
$log->info('用户登录成功');
$log->notice('需要注意的情况');
$log->warning('警告:磁盘空间不足');
$log->error('数据库连接失败', ['db' => 'mysql']);
$log->critical('系统严重错误');
$log->alert('需要立即处理');
$log->emergency('系统不可用');
手动实现的日志分级类
如果你不想引入外部库:
<?php
class Logger {
const DEBUG = 100;
const INFO = 200;
const NOTICE = 250;
const WARNING = 300;
const ERROR = 400;
const CRITICAL = 500;
const ALERT = 550;
const EMERGENCY = 600;
private $minLevel;
private $logPath;
private $levelNames = [
100 => 'DEBUG',
200 => 'INFO',
250 => 'NOTICE',
300 => 'WARNING',
400 => 'ERROR',
500 => 'CRITICAL',
550 => 'ALERT',
600 => 'EMERGENCY'
];
public function __construct($logPath, $minLevel = self::DEBUG) {
$this->logPath = $logPath;
$this->minLevel = $minLevel;
if (!is_dir(dirname($logPath))) {
mkdir(dirname($logPath), 0755, true);
}
}
public function log($level, $message, array $context = []) {
if ($level < $this->minLevel) {
return; // 低于最低级别,不记录
}
// 替换上下文占位符 {key}
if (!empty($context)) {
$message = preg_replace_callback('/\{([^}]+)\}/', function($matches) use ($context) {
return isset($context[$matches[1]]) ? $context[$matches[1]] : $matches[0];
}, $message);
}
$levelName = $this->levelNames[$level] ?? 'UNKNOWN';
$timestamp = date('Y-m-d H:i:s');
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
$file = $trace[1]['file'] ?? 'unknown';
$line = $trace[1]['line'] ?? 0;
$logEntry = sprintf(
"[%s] %s: %s (in %s:%d)\n",
$timestamp,
$levelName,
$message,
$file,
$line
);
// 按级别分文件存储
$levelFile = $this->logPath . '/' . strtolower($levelName) . '.log';
file_put_contents($levelFile, $logEntry, FILE_APPEND | LOCK_EX);
// 同时写入综合日志
file_put_contents($this->logPath . '/app.log', $logEntry, FILE_APPEND | LOCK_EX);
}
public function debug($message, array $context = []) {
$this->log(self::DEBUG, $message, $context);
}
public function info($message, array $context = []) {
$this->log(self::INFO, $message, $context);
}
public function warning($message, array $context = []) {
$this->log(self::WARNING, $message, $context);
}
public function error($message, array $context = []) {
$this->log(self::ERROR, $message, $context);
}
}
// 使用示例
$logger = new Logger(__DIR__ . '/logs', Logger::INFO);
$logger->debug('这个不会记录'); // 因为最低级别是INFO
$logger->info('用户 #{user_id} 登录成功', ['user_id' => 123]);
$logger->error('数据库查询失败: {error}', ['error' => 'Connection refused']);
使用环境变量控制日志级别
<?php
class EnvironmentLogger {
private $environment;
private $levelMap;
public function __construct() {
$this->environment = getenv('APP_ENV') ?: 'production';
// 不同环境设置不同级别
$this->levelMap = [
'development' => Logger::DEBUG,
'staging' => Logger::INFO,
'production' => Logger::WARNING
];
}
public function getMinLevel() {
return $this->levelMap[$this->environment] ?? Logger::WARNING;
}
}
// 使用
$envLogger = new EnvironmentLogger();
$logger = new Logger(__DIR__ . '/logs', $envLogger->getMinLevel());
配置文件驱动的高级方案
<?php
// config/logging.php
return [
'default' => 'stack',
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['daily', 'slack']
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/app.log'),
'level' => 'debug',
'days' => 14
],
'slack' => [
'driver' => 'slack',
'url' => env('LOG_SLACK_WEBHOOK_URL'),
'level' => 'critical'
],
'emergency' => [
'path' => storage_path('logs/emergency.log')
]
]
];
// 使用(类似 Laravel 的实现)
class LogManager {
private $channels = [];
private $config;
public function __construct($config) {
$this->config = $config;
}
public function channel($name = null) {
$name = $name ?: $this->config['default'];
if (!isset($this->channels[$name])) {
$this->channels[$name] = $this->createChannel($name);
}
return $this->channels[$name];
}
private function createChannel($name) {
$config = $this->config['channels'][$name];
switch ($config['driver']) {
case 'daily':
return new DailyLogger($config['path'], $config['level'], $config['days']);
case 'slack':
return new SlackLogger($config['url'], $config['level']);
default:
return new Logger($config['path'], $config['level']);
}
}
}
实际项目中的最佳实践
<?php
// 1. 创建全局日志实例(单例)
class AppLogger {
private static $instance = null;
private $logger;
private function __construct() {
$logLevel = $this->determineLogLevel();
$this->logger = new Logger(__DIR__ . '/logs', $logLevel);
}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance->logger;
}
private function determineLogLevel() {
// 从环境变量或配置文件读取
$env = getenv('APP_ENV') ?: 'production';
$levels = [
'development' => Logger::DEBUG,
'testing' => Logger::DEBUG,
'staging' => Logger::INFO,
'production' => Logger::WARNING
];
return $levels[$env] ?? Logger::WARNING;
}
}
// 2. 实际使用
$logger = AppLogger::getInstance();
try {
// 业务逻辑
$logger->info('开始处理请求', ['url' => $_SERVER['REQUEST_URI']]);
// ...
$logger->info('请求完成', ['duration' => $duration]);
} catch (Exception $e) {
$logger->error('请求处理失败', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
if ($e instanceof DatabaseException) {
$logger->critical('数据库不可用', [
'db_host' => DB_HOST,
'error' => $e->getMessage()
]);
}
}
日志分级的最佳实践建议
// 日志级别使用建议表
$usageGuide = [
'debug' => [
'用途' => '开发调试,详细运行信息',
'建议' => '只在开发环境启用'
],
'info' => [
'用途' => '常规操作日志,用户行为记录',
'建议' => '开发环境全开,生产环境选择性记录'
],
'notice' => [
'用途' => '正常但重要的事件',
'建议' => '所有环境都记录'
],
'warning' => [
'用途' => '潜在问题,不影响运行',
'建议' => '生产环境必须记录'
],
'error' => [
'用途' => '运行时错误,需要处理',
'建议' => '立即通知开发团队'
],
'critical' => [
'用途' => '系统级严重错误',
'建议' => '触发报警机制'
],
'alert' => [
'用途' => '需要立即处理',
'建议' => '发送短信/电话通知'
],
'emergency' => [
'用途' => '系统不可用',
'建议' => '全员紧急通知'
]
];
集成到框架中
// 以 Slim 框架为例
$container['logger'] = function($c) {
$logger = new Logger('slim_app');
// 文件处理器
$fileHandler = new StreamHandler(__DIR__ . '/logs/app.log', Logger::DEBUG);
$logger->pushHandler($fileHandler);
// 错误邮件处理器(仅生产环境)
if ($c->get('settings')['environment'] === 'production') {
$mailHandler = new SwiftMailerHandler(
new Swift_Mailer($transport),
'admin@example.com',
'应用错误通知',
Logger::ERROR
);
$logger->pushHandler($mailHandler);
}
return $logger;
};
总结建议
- 使用 PSR-3 标准:便于与其他库集成
- 配置驱动:通过环境变量控制日志级别
- 区分环境:开发环境记录所有级别,生产环境只记录 WARNING 以上
- 合理使用上下文:记录相关数据便于调试
- 考虑性能:使用异步写入或队列处理高并发场景
- 轮转日志:避免单个日志文件过大
选择哪种方案取决于项目规模:
- 小项目:手动实现简单类即可
- 中型项目:使用 Monolog
- 大型项目:使用框架内置日志系统或 ELK 等专业方案
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播