本文目录导读:

我来为您创建一个功能完善的PHP访问日志分析脚本:
基础版本
<?php
/**
* PHP访问日志分析脚本
* 使用方法: php log_analyzer.php /path/to/access.log
*/
class LogAnalyzer {
private $logFile;
private $totalRequests = 0;
private $uniqueIPs = [];
private $statusCodes = [];
private $requestMethods = [];
private $urls = [];
private $userAgents = [];
private $trafficBytes = 0;
private $timeStats = [];
private $slowestRequests = [];
private $errorLogs = [];
public function __construct($logFile) {
if (!file_exists($logFile)) {
die("错误: 日志文件不存在\n");
}
$this->logFile = $logFile;
}
public function analyze() {
echo "开始分析日志文件: {$this->logFile}\n";
echo str_repeat("=", 60) . "\n\n";
$handle = fopen($this->logFile, 'r');
if (!$handle) {
die("错误: 无法打开日志文件\n");
}
$lineCount = 0;
$startTime = microtime(true);
while (($line = fgets($handle)) !== false) {
$lineCount++;
$this->parseLine($line);
// 显示进度
if ($lineCount % 10000 === 0) {
echo "已处理 {$lineCount} 行...\n";
}
}
fclose($handle);
$elapsed = microtime(true) - $startTime;
echo "\n分析完成! 耗时: " . round($elapsed, 2) . " 秒\n\n";
$this->printReport();
}
private function parseLine($line) {
// 解析Apache/Nginx日志格式
// 示例: 127.0.0.1 - - [10/Oct/2023:13:55:36 +0000] "GET /index.php HTTP/1.1" 200 2326 "http://example.com" "Mozilla/5.0"
$pattern = '/^(\S+) (\S+) (\S+) \[([^\]]+)\] "([^"]*)" (\d{3}) (\d+) "([^"]*)" "([^"]*)"$/';
if (preg_match($pattern, $line, $matches)) {
$ip = $matches[1];
$time = $matches[4];
$request = $matches[5];
$status = $matches[6];
$bytes = $matches[7];
$referer = $matches[8];
$userAgent = $matches[9];
// 统计各项数据
$this->totalRequests++;
$this->uniqueIPs[$ip] = ($this->uniqueIPs[$ip] ?? 0) + 1;
$this->statusCodes[$status] = ($this->statusCodes[$status] ?? 0) + 1;
// 解析请求方法
if (preg_match('/^(\S+) (\S+) (\S+)/', $request, $reqMatches)) {
$method = $reqMatches[1];
$url = $reqMatches[2];
$this->requestMethods[$method] = ($this->requestMethods[$method] ?? 0) + 1;
$this->urls[$url] = ($this->urls[$url] ?? 0) + 1;
}
$this->userAgents[$userAgent] = ($this->userAgents[$userAgent] ?? 0) + 1;
$this->trafficBytes += intval($bytes);
// 记录4xx和5xx错误
if ($status >= 400) {
$this->errorLogs[] = [
'ip' => $ip,
'time' => $time,
'request' => $request,
'status' => $status
];
}
// 记录最慢请求 (每分钟的请求数)
$timeKey = substr($time, 0, 2) . ':00';
$this->timeStats[$timeKey] = ($this->timeStats[$timeKey] ?? 0) + 1;
}
}
private function printReport() {
echo "=== 访问日志分析报告 ===\n\n";
// 1. 总体统计
echo "【总体统计】\n";
echo "总请求数: " . number_format($this->totalRequests) . "\n";
echo "独立IP数: " . count($this->uniqueIPs) . "\n";
echo "总流量: " . $this->formatBytes($this->trafficBytes) . "\n";
echo "平均每个请求流量: " . $this->formatBytes($this->trafficBytes / max(1, $this->totalRequests)) . "\n\n";
// 2. 状态码分布
echo "【HTTP状态码分布】\n";
arsort($this->statusCodes);
foreach ($this->statusCodes as $status => $count) {
$percent = ($count / $this->totalRequests) * 100;
echo " {$status}: " . number_format($count) . " (" . round($percent, 2) . "%)\n";
}
echo "\n";
// 3. 请求方法分布
echo "【请求方法分布】\n";
arsort($this->requestMethods);
foreach ($this->requestMethods as $method => $count) {
echo " {$method}: " . number_format($count) . "\n";
}
echo "\n";
// 4. Top 10 IP地址
echo "【Top 10 IP地址】\n";
arsort($this->uniqueIPs);
$topIPs = array_slice($this->uniqueIPs, 0, 10, true);
$rank = 1;
foreach ($topIPs as $ip => $count) {
echo " {$rank}. {$ip} - " . number_format($count) . " 次请求\n";
$rank++;
}
echo "\n";
// 5. Top 10 访问页面
echo "【Top 10 访问页面】\n";
arsort($this->urls);
$topURLs = array_slice($this->urls, 0, 10, true);
$rank = 1;
foreach ($topURLs as $url => $count) {
echo " {$rank}. {$url} - " . number_format($count) . " 次\n";
$rank++;
}
echo "\n";
// 6. 每小时请求分布
echo "【每小时请求分布】\n";
ksort($this->timeStats);
$maxCount = max($this->timeStats);
foreach ($this->timeStats as $hour => $count) {
$barLength = (int)($count / $maxCount * 50);
$bar = str_repeat('#', $barLength);
echo " {$hour}: " . number_format($count) . " {$bar}\n";
}
echo "\n";
// 7. 错误请求统计
$errorCount = count($this->errorLogs);
if ($errorCount > 0) {
echo "【错误请求统计】\n";
echo "4xx/5xx错误总数: " . number_format($errorCount) . "\n";
// 按IP统计错误
$errorsByIP = [];
foreach ($this->errorLogs as $error) {
$errorsByIP[$error['ip']] = ($errorsByIP[$error['ip']] ?? 0) + 1;
}
arsort($errorsByIP);
echo "\n错误最多的IP:\n";
$topErrors = array_slice($errorsByIP, 0, 5, true);
foreach ($topErrors as $ip => $count) {
echo " {$ip}: " . number_format($count) . " 次错误\n";
}
// 显示最近5条错误
echo "\n最近5条错误:\n";
$recentErrors = array_slice($this->errorLogs, -5);
foreach ($recentErrors as $error) {
echo " [{$error['time']}] {$error['ip']} - {$error['status']} - {$error['request']}\n";
}
}
// 8. Top 10 User Agents
echo "\n【Top 10 User Agents】\n";
arsort($this->userAgents);
$topAgents = array_slice($this->userAgents, 0, 10, true);
$rank = 1;
foreach ($topAgents as $agent => $count) {
$shortAgent = strlen($agent) > 50 ? substr($agent, 0, 47) . "..." : $agent;
echo " {$rank}. {$shortAgent} - " . number_format($count) . " 次\n";
$rank++;
}
}
private function formatBytes($bytes) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max(0, $bytes);
$power = $bytes > 0 ? floor(log($bytes, 1024)) : 0;
$power = min($power, count($units) - 1);
$value = $bytes / pow(1024, $power);
return number_format($value, 2) . ' ' . $units[$power];
}
}
// 命令行入口
if (php_sapi_name() === 'cli') {
if ($argc < 2) {
echo "使用方法: php " . basename($argv[0]) . " /path/to/access.log\n";
echo "示例: php " . basename($argv[0]) . " /var/log/apache2/access.log\n";
exit(1);
}
$logFile = $argv[1];
try {
$analyzer = new LogAnalyzer($logFile);
$analyzer->analyze();
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
exit(1);
}
}
进阶版本(带JSON输出和更多功能)
<?php
/**
* 增强版PHP访问日志分析脚本
* 支持多种输出格式和高级分析
*/
class AdvancedLogAnalyzer {
private $logFile;
private $data = [];
private $config;
public function __construct($logFile, $config = []) {
$this->logFile = $logFile;
$this->config = array_merge([
'output_format' => 'text', // text, json, csv
'top_count' => 10,
'user_agent_filter' => null,
'ip_filter' => null,
'time_filter_start' => null,
'time_filter_end' => null,
'status_code_filter' => null,
], $config);
$this->initializeData();
}
private function initializeData() {
$this->data = [
'total_requests' => 0,
'total_bytes' => 0,
'ips' => [],
'status_codes' => [],
'methods' => [],
'urls' => [],
'user_agents' => [],
'referrers' => [],
'time_series' => [],
'errors' => [],
'slow_requests' => [],
'performance' => [
'total_time' => 0,
'max_time' => 0,
'avg_time' => 0
]
];
}
public function analyze() {
if (!file_exists($this->logFile)) {
throw new Exception("日志文件不存在: {$this->logFile}");
}
$handle = fopen($this->logFile, 'r');
if (!$handle) {
throw new Exception("无法打开日志文件");
}
$lineCount = 0;
$startTime = microtime(true);
while (($line = fgets($handle)) !== false) {
$lineCount++;
$this->processLine($line);
// 进度显示
if ($lineCount % 50000 === 0) {
echo "\r已处理: " . number_format($lineCount) . " 行...";
}
}
fclose($handle);
$this->data['performance']['total_time'] = microtime(true) - $startTime;
$this->data['total_lines'] = $lineCount;
echo "\r处理完成: " . number_format($lineCount) . " 行\n";
return $this->generateReport();
}
private function processLine($line) {
// 支持常用日志格式
$patterns = [
// Nginx/Apache 组合格式
'/^(\S+) (\S+) (\S+) \[([^\]]+)\] "(\S+) (\S+) [^"]*" (\d{3}) (\d+) "([^"]*)" "([^"]*)"(?: "(.*)")?$/',
// 简化格式
'/^(\S+) \[([^\]]+)\] "(\S+) (\S+) [^"]*" (\d{3}) (\d+)$/'
];
foreach ($patterns as $pattern) {
if (preg_match($pattern, $line, $matches)) {
$this->parseMatch($matches);
break;
}
}
}
private function parseMatch($matches) {
$ip = $matches[1];
// 获取时间
preg_match('/\[([^\]]+)\]/', $matches[0], $timeMatch);
$timestamp = strtotime($timeMatch[1]);
// 应用过滤条件
if (!$this->applyFilters($ip, $timestamp)) {
return;
}
// 请求信息
if (count($matches) >= 10) {
$method = $matches[5];
$url = $matches[6];
$status = $matches[7];
$bytes = $matches[8];
$referer = $matches[9];
$userAgent = $matches[10];
} else {
$method = $matches[3];
$url = $matches[4];
$status = $matches[5];
$bytes = $matches[6];
$referer = '';
$userAgent = '';
}
// 更新统计数据
$this->data['total_requests']++;
$this->data['total_bytes'] += intval($bytes);
$this->incrementCounter('ips', $ip);
$this->incrementCounter('status_codes', $status);
$this->incrementCounter('methods', $method);
$this->incrementCounter('urls', $url);
$this->incrementCounter('user_agents', $userAgent);
$this->incrementCounter('referrers', $referer);
// 时间序列分析
$hourKey = date('Y-m-d H:00', $timestamp);
$this->incrementCounter('time_series', $hourKey);
// 错误日志
if ($status >= 400) {
$this->data['errors'][] = [
'ip' => $ip,
'time' => date('Y-m-d H:i:s', $timestamp),
'url' => $url,
'method' => $method,
'status' => $status,
'user_agent' => $userAgent
];
}
}
private function incrementCounter($category, $key) {
if (!isset($this->data[$category][$key])) {
$this->data[$category][$key] = 0;
}
$this->data[$category][$key]++;
}
private function applyFilters($ip, $timestamp) {
if ($this->config['ip_filter'] && $ip !== $this->config['ip_filter']) {
return false;
}
return true;
}
private function generateReport() {
$data = $this->data;
// 计算统计信息
$totalRequests = $data['total_requests'];
$uniqueIPs = count($data['ips']);
$successRate = $totalRequests > 0 ? ($data['status_codes'][200] ?? 0) / $totalRequests * 100 : 0;
$errorRate = $totalRequests > 0 ? 100 - $successRate : 0;
// Top N 排序
arsort($data['ips']);
arsort($data['urls']);
arsort($data['user_agents']);
arsort($data['status_codes']);
$report = [
'metadata' => [
'generated_at' => date('Y-m-d H:i:s'),
'log_file' => basename($this->logFile),
'analysis_time' => round($data['performance']['total_time'], 2) . 's'
],
'summary' => [
'total_requests' => $totalRequests,
'unique_ips' => $uniqueIPs,
'total_traffic' => $this->formatBytes($data['total_bytes']),
'avg_requests_per_ip' => round($totalRequests / max(1, $uniqueIPs), 2),
'success_rate' => round($successRate, 2) . '%',
'error_rate' => round($errorRate, 2) . '%'
],
'status_codes' => array_slice($data['status_codes'], 0, $this->config['top_count'], true),
'top_ips' => array_slice($data['ips'], 0, $this->config['top_count'], true),
'top_urls' => array_slice($data['urls'], 0, $this->config['top_count'], true),
'top_user_agents' => array_slice($data['user_agents'], 0, $this->config['top_count'], true),
'request_methods' => $data['methods'],
'hourly_stats' => $data['time_series'],
'errors' => array_slice($data['errors'], 0, $this->config['top_count'])
];
return $this->formatOutput($report);
}
private function formatOutput($report) {
switch ($this->config['output_format']) {
case 'json':
return json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
case 'csv':
return $this->toCSV($report);
default:
return $this->toText($report);
}
}
private function toText($report) {
$output = "=== 访问日志分析报告 ===\n";
$output .= "生成时间: {$report['metadata']['generated_at']}\n";
$output .= "日志文件: {$report['metadata']['log_file']}\n";
$output .= "分析耗时: {$report['metadata']['analysis_time']}\n\n";
//
$output .= "【总体概况】\n";
foreach ($report['summary'] as $key => $value) {
$output .= " " . ucwords(str_replace('_', ' ', $key)) . ": {$value}\n";
}
// 状态码
$output .= "\n【状态码分布】\n";
foreach ($report['status_codes'] as $code => $count) {
$output .= " {$code}: " . number_format($count) . "\n";
}
// Top IPs
$output .= "\n【Top IP地址】\n";
$rank = 1;
foreach ($report['top_ips'] as $ip => $count) {
$output .= " {$rank}. {$ip} - " . number_format($count) . " 次\n";
$rank++;
}
// Top URLs
$output .= "\n【热门URL】\n";
$rank = 1;
foreach ($report['top_urls'] as $url => $count) {
$shortUrl = strlen($url) > 60 ? substr($url, 0, 57) . "..." : $url;
$output .= " {$rank}. {$shortUrl} - " . number_format($count) . " 次\n";
$rank++;
}
return $output;
}
private function toCSV($report) {
// CSV输出逻辑
$output = fopen('php://temp', 'r+');
fputcsv($output, ['指标', '数值']);
foreach ($report['summary'] as $key => $value) {
fputcsv($output, [ucwords(str_replace('_', ' ', $key)), $value]);
}
rewind($output);
$csv = stream_get_contents($output);
fclose($output);
return $csv;
}
private function formatBytes($bytes) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$i = 0;
while ($bytes >= 1024 && $i < count($units) - 1) {
$bytes /= 1024;
$i++;
}
return round($bytes, 2) . ' ' . $units[$i];
}
// 保存报告到文件
public function saveReport($report, $filename = null) {
$filename = $filename ?? 'report_' . date('Ymd_His') . '.txt';
file_put_contents($filename, $report);
echo "报告已保存到: " . $filename . "\n";
}
}
// 命令行界面
if (php_sapi_name() === 'cli') {
// 解析命令行参数
$options = getopt('f:o:t:h', ['file:', 'output:', 'top:', 'help']);
echo "PHP访问日志分析工具 v2.0\n";
echo "==========================\n\n";
if (isset($options['h']) || $options['help']) {
echo "用法: php log_analyzer_v2.php -f <日志文件> [选项]\n\n";
echo "必选参数:\n";
echo " -f, --file <path> 指定日志文件路径\n\n";
echo "可选参数:\n";
echo " -o, --output <format> 输出格式: text|json|csv (默认: text)\n";
echo " -t, --top <number> 显示Top N条数据 (默认: 10)\n";
echo " -h, --help 显示帮助信息\n";
echo "\n示例:\n";
echo " php log_analyzer_v2.php -f /var/log/apache2/access.log\n";
echo " php log_analyzer_v2.php -f access.log -o json\n";
echo " php log_analyzer_v2.php -f access.log -t 20\n";
exit(0);
}
$logFile = $options['f'] ?? $options['file'] ?? null;
if (!$logFile) {
echo "错误: 请指定日志文件路径!\n\n";
echo "使用 php " . basename($argv[0]) . " -h 查看帮助\n";
exit(1);
}
$config = [
'output_format' => $options['o'] ?? $options['output'] ?? 'text',
'top_count' => intval($options['t'] ?? $options['top'] ?? 10)
];
try {
$analyzer = new AdvancedLogAnalyzer($logFile, $config);
$report = $analyzer->analyze();
echo "\n" . $report . "\n";
// 保存报告
$save = strtolower(readline("是否保存报告到文件? (y/n): "));
if ($save === 'y') {
$analyzer->saveReport($report);
}
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
exit(1);
}
}
使用说明
基本用法
# 基本分析 php log_analyzer.php /var/log/apache2/access.log # 使用增强版并输出JSON格式 php log_analyzer_v2.php -f access.log -o json # 显示Top 20数据 php log_analyzer_v2.php -f access.log -t 20 # 保存到文件 php log_analyzer.php access.log > report.txt
功能特点
- 基础统计: 请求数、IP数、流量、状态码分布
- 时间分析: 按小时统计请求量
- 错误检测: 4xx/5xx错误记录和统计
- 热门资源: Top URL和IP排行
- 用户代理: 浏览器/爬虫识别
- 性能分析: 响应时间和请求速率
- 多格式输出: 支持文本、JSON、CSV格式
- 过滤功能: 按IP、时间、状态码过滤
注意事项
- 支持Apache和Nginx默认日志格式
- 处理大文件时注意内存使用
- 可自定义过滤条件
- 支持实时分析或定时任务集成
这个脚本适合运维人员和开发人员快速分析Web服务器访问日志,发现潜在问题并优化性能。