本文目录导读:

在PHP项目中实现日志轮转(Log Rotation)有多种方法,以下是几种常见的实现方案:
使用PHP内置函数手动实现
<?php
class LogRotation {
private $logFile;
private $maxSize; // 字节
private $maxFiles; // 保留的文件数
public function __construct($logFile, $maxSize = 10485760, $maxFiles = 5) {
$this->logFile = $logFile;
$this->maxSize = $maxSize; // 默认10MB
$this->maxFiles = $maxFiles;
}
public function write($message) {
// 检查是否需要轮转
if (file_exists($this->logFile) && filesize($this->logFile) >= $this->maxSize) {
$this->rotate();
}
// 写入日志
$timestamp = date('Y-m-d H:i:s');
$logMessage = "[{$timestamp}] {$message}" . PHP_EOL;
file_put_contents($this->logFile, $logMessage, FILE_APPEND | LOCK_EX);
}
private function rotate() {
// 删除最旧的日志文件
$oldestFile = $this->logFile . '.' . $this->maxFiles;
if (file_exists($oldestFile)) {
unlink($oldestFile);
}
// 轮转文件
for ($i = $this->maxFiles - 1; $i >= 1; $i--) {
$oldFile = $this->logFile . '.' . $i;
$newFile = $this->logFile . '.' . ($i + 1);
if (file_exists($oldFile)) {
rename($oldFile, $newFile);
}
}
// 将当前日志重命名为 .1
rename($this->logFile, $this->logFile . '.1');
}
}
// 使用示例
$logger = new LogRotation('/var/log/app.log', 10485760, 5);
$logger->write('这是一条测试日志');
?>
使用Monolog库(推荐)
Monolog是PHP最流行的日志库,内置日志轮转支持:
composer require monolog/monolog
<?php
require 'vendor/autoload.php';
use Monolog\Logger;
use Monolog\Handler\RotatingFileHandler;
// 创建日志实例
$log = new Logger('app');
// 创建轮转文件处理器
// 参数:日志文件路径,最大文件数,日志级别
$handler = new RotatingFileHandler(
'/var/log/app.log', // 日志文件路径
7, // 保留最近7天的日志
Logger::DEBUG // 日志级别
);
// 设置日志文件名格式(可选)
$handler->setFilenameFormat(
'{filename}-{date}',
'Y-m-d'
);
$log->pushHandler($handler);
// 使用示例
$log->info('这是一条信息日志');
$log->error('这是一条错误日志', ['user_id' => 123]);
?>
基于时间的轮转(按天轮转)
<?php
class DailyLogRotation {
private $logDir;
private $logFilePrefix;
private $retentionDays;
public function __construct($logDir, $prefix = 'app', $retentionDays = 30) {
$this->logDir = rtrim($logDir, '/');
$this->logFilePrefix = $prefix;
$this->retentionDays = $retentionDays;
if (!is_dir($this->logDir)) {
mkdir($this->logDir, 0755, true);
}
}
public function write($message) {
$todayLog = $this->getTodayLogFile();
$timestamp = date('Y-m-d H:i:s');
$logMessage = "[{$timestamp}] {$message}" . PHP_EOL;
file_put_contents($todayLog, $logMessage, FILE_APPEND | LOCK_EX);
// 清理过期日志
$this->cleanOldLogs();
}
private function getTodayLogFile() {
$date = date('Y-m-d');
return "{$this->logDir}/{$this->logFilePrefix}-{$date}.log";
}
private function cleanOldLogs() {
$files = glob("{$this->logDir}/{$this->logFilePrefix}-*.log");
$now = time();
foreach ($files as $file) {
if (is_file($file)) {
// 从文件名提取日期
preg_match('/-(\d{4}-\d{2}-\d{2})\.log$/', $file, $matches);
if (isset($matches[1])) {
$fileDate = strtotime($matches[1]);
$daysOld = ($now - $fileDate) / 86400;
if ($daysOld > $this->retentionDays) {
unlink($file);
}
}
}
}
}
}
// 使用示例
$logger = new DailyLogRotation('/var/log/app', 'app', 30);
$logger->write('用户登录成功');
?>
使用操作系统级日志轮转(推荐用于生产环境)
Linux logrotate配置
创建 /etc/logrotate.d/php-app:
/var/log/php-app/*.log {
daily # 每天轮转
missingok # 如果日志文件丢失,继续执行不报错
rotate 7 # 保留7个备份
compress # 压缩旧日志
delaycompress # 延迟压缩(下一次轮转时压缩)
notifempty # 如果日志为空,不轮转
create 0640 www-data www-data # 创建新日志文件的权限和所有者
sharedscripts # 所有日志文件共享一个脚本
postrotate
/bin/kill -USR1 `cat /var/run/php-fpm.pid 2>/dev/null` 2>/dev/null || true
endscript
}
高级配置示例:完整的日志管理类
<?php
class AdvancedLogger {
private $logDir;
private $currentLogFile;
private $maxFileSize;
private $maxFiles;
private $dateFormat;
public function __construct($config = []) {
$this->logDir = $config['log_dir'] ?? '/var/log/app';
$this->maxFileSize = $config['max_file_size'] ?? 10485760; // 10MB
$this->maxFiles = $config['max_files'] ?? 5;
$this->dateFormat = $config['date_format'] ?? 'Y-m-d H:i:s';
if (!is_dir($this->logDir)) {
mkdir($this->logDir, 0755, true);
}
$this->currentLogFile = $this->logDir . '/app.log';
}
public function log($level, $message, $context = []) {
$this->checkRotation();
$timestamp = date($this->dateFormat);
$contextStr = !empty($context) ? ' ' . json_encode($context) : '';
$logMessage = "[{$timestamp}] [{$level}] {$message}{$contextStr}" . PHP_EOL;
// 写入文件
file_put_contents($this->currentLogFile, $logMessage, FILE_APPEND | LOCK_EX);
// 同时输出到控制台(开发环境)
if (php_sapi_name() === 'cli') {
echo $logMessage;
}
}
private function checkRotation() {
if (!file_exists($this->currentLogFile)) {
return;
}
if (filesize($this->currentLogFile) >= $this->maxFileSize) {
$this->rotate();
}
}
private function rotate() {
$info = pathinfo($this->currentLogFile);
$base = $info['dirname'] . '/' . $info['filename'];
// 删除最旧的文件
$oldestFile = $base . '.' . $this->maxFiles . '.log';
if (file_exists($oldestFile)) {
unlink($oldestFile);
}
// 轮转文件
for ($i = $this->maxFiles - 1; $i >= 1; $i--) {
$oldFile = $base . '.' . $i . '.log';
$newFile = $base . '.' . ($i + 1) . '.log';
if (file_exists($oldFile)) {
rename($oldFile, $newFile);
}
}
// 压缩日志(可选)
$logFile = $base . '.log';
if (file_exists($logFile)) {
rename($logFile, $base . '.1.log');
$this->compressLog($base . '.1.log');
}
}
private function compressLog($file) {
if (function_exists('gzopen')) {
$gzFile = $file . '.gz';
$fp = fopen($file, 'r');
$zp = gzopen($gzFile, 'w9');
while (!feof($fp)) {
gzwrite($zp, fread($fp, 8192));
}
fclose($fp);
gzclose($zp);
unlink($file);
}
}
// 便捷方法
public function info($message, $context = []) {
$this->log('INFO', $message, $context);
}
public function error($message, $context = []) {
$this->log('ERROR', $message, $context);
}
public function warning($message, $context = []) {
$this->log('WARNING', $message, $context);
}
public function debug($message, $context = []) {
$this->log('DEBUG', $message, $context);
}
}
// 使用示例
$logger = new AdvancedLogger([
'log_dir' => '/var/log/myapp',
'max_file_size' => 5242880, // 5MB
'max_files' => 10
]);
$logger->info('应用启动');
$logger->error('数据库连接失败', ['db' => 'mysql', 'host' => 'localhost']);
?>
建议
- 生产环境:推荐使用
logrotate系统工具,性能更好 - 开发环境:使用 Monolog 或自定义轮转逻辑
- 重要考虑:
- 确保日志目录有适当的权限
- 考虑使用异步写入提高性能
- 监控磁盘空间使用情况
- 设置合理的保留策略
选择哪种方案取决于你的具体需求、项目规模和部署环境。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播