首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 滾動日誌類別實作:依大小與數量自動輪替

     編輯:PHP 相關知識

PHP 滾動日誌類別實作:依大小與數量自動輪替

傳統按日期分檔的日誌易產生龐大單一檔案,難以閱讀與維護。本文提供單例模式的滾動日誌類別,支援依檔案大小與檔案數量自動輪替,並提供多級別記錄功能。

核心特性

  • 單例模式:全域唯一實例,避免重複開啟檔案控制代碼。
  • 大小輪替:當前日誌檔(xxx.log)達到 maxFileSize 時自動改名為 xxx_1.log,舊檔依序遞增(_1 → _2 → ...)。
  • 數量限制:最多保留 maxFileNum 個檔案(含當前檔),超過時刪除最舊檔案。
  • 三級別:DEBUG、MSG、ERR,可依設定層級過濾。
  • 高效能寫入:使用 fopen('a+b') 追加寫入,自動釋放鎖定。

完整類別代碼

<?php
final class LOGS {
  const LOGS_DEBUG = 0;
  const LOGS_MSG   = 1;
  const LOGS_ERR   = 2;

  private $level;
  private $maxFileNum;
  private $maxFileSize;
  private $logPath;
  private $filePrefix;

  private static $instance = null;

  private function __construct() {}

  public static function getInstance() {
    if (self::$instance === null) {
      self::$instance = new self();
    }
    return self::$instance;
  }

  public function init($level, $maxNum, $maxSize, $logPath, $filePrefix) {
    $level = intval($level);
    $maxNum = intval($maxNum);
    $maxSize = intval($maxSize);

    if (!is_dir($logPath)) {
      @mkdir($logPath, 0777, true);
    }
    if (!in_array($level, [self::LOGS_DEBUG, self::LOGS_MSG, self::LOGS_ERR]) ||
        $maxNum <= 0 || $maxSize <= 0 || !is_dir($logPath)) {
      return false;
    }

    $this->level = $level;
    $this->maxFileNum = $maxNum;
    $this->maxFileSize = $maxSize;
    $this->logPath = rtrim($logPath, DIRECTORY_SEPARATOR);
    $this->filePrefix = $filePrefix;
    return true;
  }

  private function formatTime() {
    $ustime = explode(" ", microtime());
    return "[" . date('Y-m-d H:i:s', time()) . "." . ($ustime[0] * 1000) . "]";
  }

  private function rotateIfNeeded() {
    $currentFile = $this->logPath . DIRECTORY_SEPARATOR . $this->filePrefix . ".log";
    clearstatcache();
    if (!file_exists($currentFile) || filesize($currentFile) < $this->maxFileSize) {
      return;
    }

    // 尋找現有最大索引
    $index = 1;
    for (; $index < $this->maxFileNum; $index++) {
      if (!file_exists($this->logPath . DIRECTORY_SEPARATOR . $this->filePrefix . "_" . $index . ".log")) {
        break;
      }
    }

    // 已達上限,從倒數第二個開始向後搬移(覆蓋最舊)
    if ($index == $this->maxFileNum) {
      $index--;
    }

    for (; $index > 1; $index--) {
      $old = $this->logPath . DIRECTORY_SEPARATOR . $this->filePrefix . "_" . ($index - 1) . ".log";
      $new = $this->logPath . DIRECTORY_SEPARATOR . $this->filePrefix . "_" . $index . ".log";
      @rename($old, $new);
    }

    // 將當前檔改名為 _1.log
    @rename($currentFile, $this->logPath . DIRECTORY_SEPARATOR . $this->filePrefix . "_1.log");
  }

  public function log($str) {
    $this->rotateIfNeeded();
    $path = $this->logPath . DIRECTORY_SEPARATOR . $this->filePrefix . ".log";
    $fp = fopen($path, "a+b");
    if ($fp) {
      fwrite($fp, $str, strlen($str));
      fclose($fp);
    }
    return true;
  }

  public function debug($msg, $file, $line) {
    if ($this->level <= self::LOGS_DEBUG) {
      $this->log($this->formatTime() . "[{$file}:{$line}] DEBUG: {$msg}\n");
    }
  }

  public function msg($msg, $file, $line) {
    if ($this->level <= self::LOGS_MSG) {
      $this->log($this->formatTime() . "[{$file}:{$line}] MSG: {$msg}\n");
    }
  }

  public function err($msg, $file, $line) {
    if ($this->level <= self::LOGS_ERR) {
      $this->log($this->formatTime() . "[{$file}:{$line}] ERR: {$msg}\n");
    }
  }
}

// 使用範例
$logs = LOGS::getInstance();
$logs->init(LOGS::LOGS_MSG, 5, 200, __DIR__, 'app'); // MSG 層級、最多 5 檔、每檔 200 bytes
$logs->msg("使用者登入", __FILE__, __LINE__);
$logs->debug("除錯訊息", __FILE__, __LINE__); // 不會寫入,因層級為 MSG
?>

執行結果說明

持續執行上述範例,目錄下將產生:

app_4.log
app_3.log
app_2.log
app_1.log
app.log          <-- 最新日誌

每個檔案不超過 200 bytes,最多 5 個檔案,自動淘汰最舊記錄。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved