Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions .agent/plans/2026-02-07-logging-system-refactor.md
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,158 @@ class Logger
fclose($handle);
return $logs;
}

/**
* 古いログファイルをgzip圧縮(ストレージ節約)
*
* @param int $days 何日以上前のログを圧縮するか(デフォルト: 7日)
* @return array 圧縮結果の統計情報
*/
public function compressOldLogs($days = 7)
{
$cutoffTime = time() - ($days * 86400);
$result = [
'count' => 0,
'before_size' => 0.0,
'after_size' => 0.0,
'saved_size' => 0.0,
'ratio' => 0.0,
];

$this->compressDirectory($this->logDir, $cutoffTime, $result);

// 削減率を計算
if ($result['before_size'] > 0) {
$result['ratio'] = round(($result['saved_size'] / $result['before_size']) * 100, 1);
}

// サイズをMB単位に変換
$result['before_size'] = round($result['before_size'] / 1048576, 2);
$result['after_size'] = round($result['after_size'] / 1048576, 2);
$result['saved_size'] = round($result['saved_size'] / 1048576, 2);

return $result;
}

/**
* ディレクトリを再帰的に走査して古いログファイルを圧縮
*
* @param string $dir 対象ディレクトリ
* @param int $cutoffTime 圧縮対象の閾値タイムスタンプ
* @param array &$result 統計情報(参照渡し)
*/
private function compressDirectory($dir, $cutoffTime, &$result)
{
if (!is_dir($dir)) {
return;
}

$items = scandir($dir);
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}

$path = $dir . $item;
if (is_dir($path)) {
$this->compressDirectory($path . '/', $cutoffTime, $result);
} elseif (is_file($path) && preg_match('/\\.log(\\.\\d+)?$/', $item)) {
// 既に圧縮済みのファイルはスキップ
if (preg_match('/\\.gz$/', $item)) {
continue;
}

// カットオフ時刻より古いファイルのみ圧縮
if (filemtime($path) < $cutoffTime) {
$beforeSize = filesize($path);
$gzPath = $path . '.gz';

// gzip圧縮
$fpIn = fopen($path, 'rb');
$fpOut = gzopen($gzPath, 'wb9'); // 最高圧縮率
if ($fpIn && $fpOut) {
while (!feof($fpIn)) {
gzwrite($fpOut, fread($fpIn, 8192));
}
fclose($fpIn);
gzclose($fpOut);

// 元ファイルを削除
unlink($path);

// 統計情報を更新
$afterSize = filesize($gzPath);
$result['count']++;
$result['before_size'] += $beforeSize;
$result['after_size'] += $afterSize;
$result['saved_size'] += ($beforeSize - $afterSize);
}
}
}
}
}

/**
* 保持期間を超えた古いログファイルを削除
*
* @param int $days 保持期間(日数)
* @param bool $dryRun trueの場合は削除せず対象ファイルのみ返す(プレビュー)
* @return array 削除結果の統計情報
*/
public function deleteOldLogs($days, $dryRun = false)
{
$cutoffTime = time() - ($days * 86400);
$result = [
'count' => 0,
'size' => 0.0,
'files' => [],
];

// 削除対象ファイルを収集(統計情報とプレビュー用)
$this->collectOldFiles($this->logDir, $cutoffTime, $result);

// dry-runモードでない場合のみ実際に削除
if (!$dryRun) {
$this->cleanDirectory($this->logDir, $cutoffTime);
}

// サイズをMB単位に変換
$result['size'] = round($result['size'] / 1048576, 2);

return $result;
}

/**
* 古いログファイルを収集(削除プレビュー用)
*
* @param string $dir 対象ディレクトリ
* @param int $cutoffTime 削除対象の閾値タイムスタンプ
* @param array &$result 統計情報(参照渡し)
*/
private function collectOldFiles($dir, $cutoffTime, &$result)
{
if (!is_dir($dir)) {
return;
}

$items = scandir($dir);
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}

$path = $dir . $item;
if (is_dir($path)) {
$this->collectOldFiles($path . '/', $cutoffTime, $result);
} elseif (is_file($path)) {
if (filemtime($path) < $cutoffTime) {
$result['count']++;
$result['size'] += filesize($path);
$result['files'][] = str_replace($this->logDir, '', $path);
}
}
}
}
}
```

Expand Down