如何用PHP项目实现日志压缩?

wen java案例 2

本文目录导读:

如何用PHP项目实现日志压缩?

  1. 使用PHP内置的压缩函数
  2. 批量压缩和归档
  3. 带日志轮转的压缩
  4. 使用exec执行系统命令
  5. 定时任务实现自动化压缩
  6. 使用建议

我来介绍几种在PHP项目中实现日志压缩的方法:

使用PHP内置的压缩函数

gzip压缩

<?php
class LogCompressor {
    /**
     * 压缩单个日志文件
     */
    public function compressLogFile($sourceFile, $destinationFile = null) {
        if (!file_exists($sourceFile)) {
            throw new Exception("源文件不存在");
        }
        $destinationFile = $destinationFile ?? $sourceFile . '.gz';
        $data = file_get_contents($sourceFile);
        $compressed = gzencode($data, 9);
        file_put_contents($destinationFile, $compressed);
        // 可选:删除原文件
        // unlink($sourceFile);
        return $destinationFile;
    }
    /**
     * 解压缩日志文件
     */
    public function decompressLogFile($compressedFile, $destinationDir = null) {
        if (!file_exists($compressedFile)) {
            throw new Exception("压缩文件不存在");
        }
        $destinationDir = $destinationDir ?? dirname($compressedFile);
        // 读取压缩文件
        $compressed = file_get_contents($compressedFile);
        $data = gzdecode($compressed);
        // 生成解压后的文件名
        $basename = basename($compressedFile, '.gz');
        $destinationFile = $destinationDir . '/' . $basename;
        file_put_contents($destinationFile, $data);
        return $destinationFile;
    }
}

bzip2压缩

<?php
function compressWithBzip2($sourceFile, $destinationFile = null) {
    $destinationFile = $destinationFile ?? $sourceFile . '.bz2';
    // 使用bzcompress
    $data = file_get_contents($sourceFile);
    $compressed = bzcompress($data, 9);
    file_put_contents($destinationFile, $compressed);
    return $destinationFile;
}
function decompressBzip2($compressedFile, $destinationFile = null) {
    $data = file_get_contents($compressedFile);
    $decompressed = bzdecompress($data);
    $destinationFile = $destinationFile ?? preg_replace('/\.bz2$/', '', $compressedFile);
    file_put_contents($destinationFile, $decompressed);
    return $destinationFile;
}

批量压缩和归档

<?php
class LogArchiver {
    private $logDir;
    private $archiveDir;
    public function __construct($logDir, $archiveDir) {
        $this->logDir = rtrim($logDir, '/');
        $this->archiveDir = rtrim($archiveDir, '/');
        if (!is_dir($this->archiveDir)) {
            mkdir($this->archiveDir, 0755, true);
        }
    }
    /**
     * 归档指定日期之前的日志
     */
    public function archiveOldLogs($daysOld = 7) {
        $files = glob($this->logDir . '/*.log');
        $cutoff = time() - ($daysOld * 86400);
        $archived = [];
        foreach ($files as $file) {
            if (filemtime($file) < $cutoff) {
                $archiveName = basename($file) . '.' . date('Ymd') . '.gz';
                $archivePath = $this->archiveDir . '/' . $archiveName;
                // 压缩文件
                if ($this->compressFile($file, $archivePath)) {
                    // 成功压缩后删除原文件
                    unlink($file);
                    $archived[] = $archiveName;
                }
            }
        }
        return $archived;
    }
    /**
     * 压缩文件,使用流式处理避免内存问题
     */
    private function compressFile($source, $destination) {
        try {
            $sfp = gzopen($destination, 'w9');
            $fp = fopen($source, 'rb');
            while (!feof($fp)) {
                gzwrite($sfp, fread($fp, 8192));
            }
            fclose($fp);
            gzclose($sfp);
            return true;
        } catch (Exception $e) {
            error_log("压缩失败: " . $e->getMessage());
            return false;
        }
    }
    /**
     * 创建tar归档并压缩
     */
    public function createTarArchive($startDate, $endDate = null) {
        $phar = new PharData(
            $this->archiveDir . '/logs-' . date('Ymd') . '.tar.gz',
            Phar::GZ
        );
        $files = glob($this->logDir . '/*.log');
        foreach ($files as $file) {
            $fileDate = date('Ymd', filemtime($file));
            if ($fileDate >= $startDate && 
                ($endDate === null || $fileDate <= $endDate)) {
                $phar->addFile($file, basename($file));
            }
        }
        return $phar->getPath();
    }
}

带日志轮转的压缩

<?php
class LogRotationCompressor {
    private $logDir;
    private $maxSize;
    private $maxFiles;
    public function __construct($logDir, $maxSize = 104857600, $maxFiles = 10) {
        $this->logDir = rtrim($logDir, '/');
        $this->maxSize = $maxSize; // 默认100MB
        $this->maxFiles = $maxFiles;
    }
    /**
     * 检查并压缩超过大小的日志
     */
    public function checkAndRotate($logFile) {
        if (!file_exists($logFile)) {
            return false;
        }
        // 检查文件大小
        if (filesize($logFile) < $this->maxSize) {
            return false;
        }
        $basename = basename($logFile);
        // 轮转并压缩
        for ($i = $this->maxFiles - 1; $i > 0; $i--) {
            $oldFile = $this->logDir . '/' . $basename . '.' . $i . '.gz';
            $newFile = $this->logDir . '/' . $basename . '.' . ($i + 1) . '.gz';
            if (file_exists($oldFile)) {
                rename($oldFile, $newFile);
            }
        }
        // 压缩当前日志
        $destination = $this->logDir . '/' . $basename . '.1.gz';
        $this->compressFile($logFile, $destination);
        // 清空原日志文件
        file_put_contents($logFile, '');
        return true;
    }
    private function compressFile($source, $destination) {
        $sfp = gzopen($destination, 'w9');
        $fp = fopen($source, 'rb');
        while (!feof($fp)) {
            gzwrite($sfp, fread($fp, 8192));
        }
        fclose($fp);
        gzclose($sfp);
        return true;
    }
}

使用exec执行系统命令

<?php
class SystemCommandCompressor {
    /**
     * 使用gzip命令压缩
     */
    public function compressWithGzip($file) {
        // 保留原文件
        exec("gzip -c {$file} > {$file}.gz", $output, $returnVar);
        if ($returnVar !== 0) {
            throw new Exception("gzip压缩失败: " . implode("\n", $output));
        }
        return $file . '.gz';
    }
    /**
     * 使用bzip2命令压缩
     */
    public function compressWithBzip2($file) {
        exec("bzip2 -z -k {$file}", $output, $returnVar);
        if ($returnVar !== 0) {
            throw new Exception("bzip2压缩失败: " . implode("\n", $output));
        }
        return $file . '.bz2';
    }
    /**
     * 创建tar归档
     */
    public function createTarArchive($files, $archiveName) {
        $fileList = implode(' ', $files);
        exec("tar -czf {$archiveName} {$fileList}", $output, $returnVar);
        if ($returnVar !== 0) {
            throw new Exception("tar归档失败: " . implode("\n", $output));
        }
        return $archiveName;
    }
}

定时任务实现自动化压缩

创建压缩脚本 compress_logs.php

<?php
require_once 'LogArchiver.php';
$config = [
    'log_dir' => '/var/log/myapp',
    'archive_dir' => '/var/log/archives',
    'days_old' => 7,
    'max_file_size' => 104857600, // 100MB
];
$archiver = new LogArchiver($config['log_dir'], $config['archive_dir']);
// 归档旧日志
$archived = $archiver->archiveOldLogs($config['days_old']);
echo "已归档 " . count($archived) . " 个日志文件\n";
// 检查并轮转大文件
$rotator = new LogRotationCompressor(
    $config['log_dir'], 
    $config['max_file_size']
);
$logFiles = glob($config['log_dir'] . '/*.log');
foreach ($logFiles as $logFile) {
    if ($rotator->checkAndRotate($logFile)) {
        echo "已轮转: " . basename($logFile) . "\n";
    }
}

设置cron定时任务

# 每天凌晨2点执行日志压缩
0 2 * * * /usr/bin/php /path/to/compress_logs.php >> /var/log/log_compression.log 2>&1

使用建议

  1. 大文件处理:对于大日志文件,使用流式处理避免内存溢出
  2. 保留原文件:压缩完成后建议保留原文件一段时间,确认无问题再删除
  3. 错误处理:添加完善的错误处理和日志记录
  4. 性能考虑:使用适当压缩级别(通常6-9之间)
  5. 安全性:确保压缩和解压缩操作的目录权限正确

选择哪种方式取决于你的具体需求,内置函数适合简单场景,系统命令适合高性能需求,而类封装提供了更好的可维护性。

抱歉,评论功能暂时关闭!