當前位置: 首頁> 最新文章列表> 對日誌文件中的IP、時間等字段統一格式化

對日誌文件中的IP、時間等字段統一格式化

M66 2025-05-18

在處理服務器日誌文件時,我們經常會遇到格式不統一的問題。比如IP 地址可能有前導零、時間戳格式可能不一致、請求路徑可能帶有各種參數。為了統一處理這些字段, preg_replace_callback_array是一個非常強大的工具,它允許我們對多個正則表達式分別綁定不同的回調函數,在一次遍歷中完成複雜的替換邏輯。

本文將介紹如何使用preg_replace_callback_array來統一格式化日誌中的IP、時間字段。

示例日誌格式

假設我們有如下日誌內容(簡化版):

 127.000.000.001 - - [21/Apr/2025:15:32:01 +0000] "GET /index.php?id=123 HTTP/1.1" 200
192.168.1.10 - - [21-Apr-2025 15:32:01] "POST /submit.php HTTP/1.1" 404

我們希望:

  • 將IP 地址標準化(去除前導零);

  • 將時間統一為YYYY-MM-DD HH:MM:SS格式;

  • 可選:屏蔽掉路徑中的參數,例如/index.php?id=123/index.php

使用preg_replace_callback_array實現

<?php

$log = <<<LOG
127.000.000.001 - - [21/Apr/2025:15:32:01 +0000] "GET /index.php?id=123 HTTP/1.1" 200
192.168.1.10 - - [21-Apr-2025 15:32:01] "POST /submit.php HTTP/1.1" 404
LOG;

// 定義正則與對應的處理回調
$patterns = [
    // IP 地址格式化:去掉前導零
    '/\b(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\b/' => function ($matches) {
        return implode('.', array_map('intval', array_slice($matches, 1, 4)));
    },

    // Apache 風格時間戳 [21/Apr/2025:15:32:01 +0000]
    '/\[(\d{2})\/(\w{3})\/(\d{4}):(\d{2}):(\d{2}):(\d{2}) [+\-]\d{4}\]/' => function ($matches) {
        $monthMap = [
            'Jan' => '01', 'Feb' => '02', 'Mar' => '03', 'Apr' => '04',
            'May' => '05', 'Jun' => '06', 'Jul' => '07', 'Aug' => '08',
            'Sep' => '09', 'Oct' => '10', 'Nov' => '11', 'Dec' => '12'
        ];
        return sprintf('%s-%s-%s %s:%s:%s',
            $matches[3],                         // 年
            $monthMap[$matches[2]] ?? '01',     // 月
            $matches[1],                         // 天
            $matches[4], $matches[5], $matches[6]
        );
    },

    // 天志格式中另一种时间 [21-Apr-2025 15:32:01]
    '/(\d{2})-(\w{3})-(\d{4}) (\d{2}):(\d{2}):(\d{2})/' => function ($matches) {
        $monthMap = [
            'Jan' => '01', 'Feb' => '02', 'Mar' => '03', 'Apr' => '04',
            'May' => '05', 'Jun' => '06', 'Jul' => '07', 'Aug' => '08',
            'Sep' => '09', 'Oct' => '10', 'Nov' => '11', 'Dec' => '12'
        ];
        return sprintf('%s-%s-%s %s:%s:%s',
            $matches[3],
            $monthMap[$matches[2]] ?? '01',
            $matches[1],
            $matches[4], $matches[5], $matches[6]
        );
    },

    // 去掉 URL 參數(如 /index.php?id=123 → /index.php)
    '#(GET|POST|PUT|DELETE|HEAD) (/[\w\-\/\.]+)(\?[^\s"]*)?#' => function ($matches) {
        return $matches[1] . ' ' . $matches[2];
    }
];

// 應用替換
$formatted = preg_replace_callback_array($patterns, $log);

// 輸出結果
echo nl2br(htmlspecialchars($formatted));
?>

輸出結果

運行上述腳本後,日誌內容將被格式化為:

 127.0.0.1 - - 2025-04-21 15:32:01 "GET /index.php HTTP/1.1" 200  
192.168.1.10 - - 2025-04-21 15:32:01 "POST /submit.php HTTP/1.1" 404

小結

通過preg_replace_callback_array ,我們能夠用非常優雅的方式處理日誌中多個不同格式的字段。它的優勢在於一次性處理多個模式,每個模式獨立擁有自己的回調函數,邏輯清晰,維護方便。