本文目录导读:

我来提供一个完整的PHP自定义日志类封装方案:
基础日志类实现
<?php
/**
* 自定义日志类
*/
class Logger {
// 日志级别
const DEBUG = 'DEBUG';
const INFO = 'INFO';
const WARNING = 'WARNING';
const ERROR = 'ERROR';
const CRITICAL = 'CRITICAL';
private static $instance = null;
private $logDir = '';
private $logLevel = self::DEBUG;
private $dateFormat = 'Y-m-d H:i:s';
private $filePrefix = 'app';
private $maxFileSize = 10485760; // 10MB
private $backupCount = 5;
/**
* 私有构造函数
*/
private function __construct($config = []) {
if (!empty($config)) {
$this->initialize($config);
}
}
/**
* 获取单例实例
*/
public static function getInstance($config = []) {
if (self::$instance === null) {
self::$instance = new self($config);
}
return self::$instance;
}
/**
* 初始化配置
*/
public function initialize($config) {
if (isset($config['log_dir'])) {
$this->logDir = rtrim($config['log_dir'], '/') . '/';
} else {
$this->logDir = dirname(__FILE__) . '/../logs/';
}
if (isset($config['log_level'])) {
$this->logLevel = strtoupper($config['log_level']);
}
if (isset($config['date_format'])) {
$this->dateFormat = $config['date_format'];
}
if (isset($config['file_prefix'])) {
$this->filePrefix = $config['file_prefix'];
}
if (isset($config['max_file_size'])) {
$this->maxFileSize = (int)$config['max_file_size'];
}
if (isset($config['backup_count'])) {
$this->backupCount = (int)$config['backup_count'];
}
// 创建日志目录
$this->createLogDirectory();
}
/**
* 创建日志目录
*/
private function createLogDirectory() {
if (!is_dir($this->logDir)) {
@mkdir($this->logDir, 0777, true);
}
// 按日期创建子目录
$dateDir = $this->logDir . date('Ymd') . '/';
if (!is_dir($dateDir)) {
@mkdir($dateDir, 0777, true);
}
}
/**
* 记录调试日志
*/
public function debug($message, $context = []) {
$this->log(self::DEBUG, $message, $context);
}
/**
* 记录信息日志
*/
public function info($message, $context = []) {
$this->log(self::INFO, $message, $context);
}
/**
* 记录警告日志
*/
public function warning($message, $context = []) {
$this->log(self::WARNING, $message, $context);
}
/**
* 记录错误日志
*/
public function error($message, $context = []) {
$this->log(self::ERROR, $message, $context);
}
/**
* 记录关键错误日志
*/
public function critical($message, $context = []) {
$this->log(self::CRITICAL, $message, $context);
}
/**
* 核心日志记录方法
*/
public function log($level, $message, $context = []) {
// 检查日志级别
if (!$this->isLevelAllowed($level)) {
return;
}
// 格式化消息
$formattedMessage = $this->formatMessage($level, $message, $context);
// 写入日志
$this->writeToFile($level, $formattedMessage);
}
/**
* 检查日志级别是否允许
*/
private function isLevelAllowed($level) {
$levels = [
self::DEBUG => 0,
self::INFO => 1,
self::WARNING => 2,
self::ERROR => 3,
self::CRITICAL => 4
];
return $levels[$level] >= $levels[$this->logLevel];
}
/**
* 格式化日志消息
*/
private function formatMessage($level, $message, $context = []) {
$timestamp = date($this->dateFormat);
if (!empty($context)) {
$message = $this->interpolate($message, $context);
}
// 获取调用信息
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
$caller = isset($trace[2]) ? $trace[2] : [];
$file = isset($caller['file']) ? basename($caller['file']) : 'unknown';
$line = isset($caller['line']) ? $caller['line'] : 'unknown';
return sprintf(
"[%s] [%s] [%s:%d] %s",
$timestamp,
$level,
$file,
$line,
$message
);
}
/**
* 替换消息中的占位符
*/
private function interpolate($message, $context = []) {
$replace = [];
foreach ($context as $key => $val) {
if (is_string($val) || method_exists($val, '__toString')) {
$replace['{' . $key . '}'] = $val;
}
}
return strtr($message, $replace);
}
/**
* 写入日志文件
*/
private function writeToFile($level, $message) {
$filename = $this->getLogFilename($level);
$this->rotateLogFile($filename);
$result = @file_put_contents(
$filename,
$message . PHP_EOL,
FILE_APPEND | LOCK_EX
);
if ($result === false) {
error_log("Failed to write to log file: {$filename}");
}
}
/**
* 获取日志文件名
*/
private function getLogFilename($level) {
$dateDir = $this->logDir . date('Ymd') . '/';
$levelLower = strtolower($level);
return $dateDir . "{$this->filePrefix}_{$levelLower}.log";
}
/**
* 日志文件轮转
*/
private function rotateLogFile($filename) {
if (!file_exists($filename)) {
return;
}
if (filesize($filename) < $this->maxFileSize) {
return;
}
// 删除最旧的备份
$oldestBackup = $filename . '.' . $this->backupCount;
if (file_exists($oldestBackup)) {
@unlink($oldestBackup);
}
// 轮转备份文件
for ($i = $this->backupCount - 1; $i >= 1; $i--) {
$backup = $filename . '.' . $i;
if (file_exists($backup)) {
rename($backup, $filename . '.' . ($i + 1));
}
}
// 重命名当前文件
rename($filename, $filename . '.1');
}
/**
* 防止克隆
*/
private function __clone() {}
/**
* 防止反序列化
*/
private function __wakeup() {}
}
增强版日志类(支持更多功能)
<?php
/**
* 增强版日志类
*/
class AdvancedLogger {
private static $instance = null;
private $config = [];
private $buffer = [];
// 日志级别定义
const LEVELS = [
'DEBUG' => 100,
'INFO' => 200,
'NOTICE' => 250,
'WARNING' => 300,
'ERROR' => 400,
'CRITICAL' => 500,
'ALERT' => 550,
'EMERGENCY' => 600
];
private function __construct($config = []) {
$this->config = array_merge([
'log_dir' => dirname(__FILE__) . '/../logs/',
'log_level' => 'DEBUG',
'date_format' => 'Y-m-d H:i:s',
'file_prefix' => 'app',
'max_file_size' => 10485760,
'backup_count' => 5,
'enable_buffer' => false,
'buffer_size' => 100,
'enable_email' => false,
'email_recipients' => [],
'email_level' => 'ERROR',
'enable_console' => false,
'format' => 'json', // json or text
], $config);
$this->init();
}
public static function getInstance($config = []) {
if (self::$instance === null) {
self::$instance = new self($config);
}
return self::$instance;
}
private function init() {
$this->createDirectory();
$this->registerShutdownHandler();
}
/**
* 注册关闭处理器
*/
private function registerShutdownHandler() {
register_shutdown_function(function() {
$this->flushBuffer();
});
}
/**
* 记录日志
*/
public function log($level, $message, $context = []) {
if (!$this->isLevelAllowed($level)) {
return;
}
$record = $this->createRecord($level, $message, $context);
if ($this->config['enable_buffer']) {
$this->buffer[] = $record;
if (count($this->buffer) >= $this->config['buffer_size']) {
$this->flushBuffer();
}
} else {
$this->writeRecord($record);
}
}
/**
* 创建日志记录
*/
private function createRecord($level, $message, $context = []) {
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
$caller = isset($trace[2]) ? $trace[2] : [];
$record = [
'timestamp' => date($this->config['date_format']),
'level' => $level,
'message' => $this->interpolate($message, $context),
'file' => isset($caller['file']) ? $caller['file'] : 'unknown',
'line' => isset($caller['line']) ? $caller['line'] : 'unknown',
'context' => $context,
'extra' => $this->getExtraData()
];
return $record;
}
/**
* 获取额外数据
*/
private function getExtraData() {
return [
'ip' => $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1',
'uri' => $_SERVER['REQUEST_URI'] ?? 'cli',
'method' => $_SERVER['REQUEST_METHOD'] ?? 'UNKNOWN',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
'memory_usage' => memory_get_usage(true),
'memory_peak' => memory_get_peak_usage(true),
'execution_time' => defined('START_TIME') ? microtime(true) - START_TIME : 0
];
}
/**
* 写入日志记录
*/
private function writeRecord($record) {
$formatted = $this->formatRecord($record);
$filename = $this->getLogFilename($record['level']);
$this->rotateLogFile($filename);
$this->writeToFile($filename, $formatted);
// 发送邮件告警
if ($this->config['enable_email'] &&
$this->isLevelReached($record['level'], $this->config['email_level'])) {
$this->sendEmailAlert($record);
}
// 控制台输出
if ($this->config['enable_console'] && PHP_SAPI === 'cli') {
$this->writeToConsole($formatted);
}
}
/**
* 格式化日志记录
*/
private function formatRecord($record) {
if ($this->config['format'] === 'json') {
return json_encode($record, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
} else {
return sprintf(
"[%s] [%s] [%s:%d] %s",
$record['timestamp'],
$record['level'],
basename($record['file']),
$record['line'],
$record['message']
);
}
}
/**
* 写入文件
*/
private function writeToFile($filename, $content) {
$result = @file_put_contents(
$filename,
$content . PHP_EOL,
FILE_APPEND | LOCK_EX
);
if ($result === false) {
// 写入失败时使用系统日志
error_log("Logger: Failed to write log to {$filename}: {$content}");
}
}
/**
* 写入控制台
*/
private function writeToConsole($content) {
$colors = [
'DEBUG' => "\033[36m", // Cyan
'INFO' => "\033[32m", // Green
'NOTICE' => "\033[34m", // Blue
'WARNING' => "\033[33m", // Yellow
'ERROR' => "\033[31m", // Red
'CRITICAL' => "\033[41m",// Red background
'ALERT' => "\033[45m", // Purple background
'EMERGENCY' => "\033[41m"// Red background
];
$level = json_decode($content, true)['level'] ?? 'INFO';
$color = $colors[$level] ?? "\033[0m";
$reset = "\033[0m";
echo $color . $content . $reset . PHP_EOL;
}
/**
* 刷新缓冲区
*/
public function flushBuffer() {
if (empty($this->buffer)) {
return;
}
$records = $this->buffer;
$this->buffer = [];
// 按级别分组写入
$grouped = [];
foreach ($records as $record) {
$grouped[$record['level']][] = $record;
}
foreach ($grouped as $level => $records) {
$filename = $this->getLogFilename($level);
$this->rotateLogFile($filename);
$content = '';
foreach ($records as $record) {
$content .= $this->formatRecord($record) . PHP_EOL;
}
$this->writeToFile($filename, $content);
}
}
/**
* 发送邮件告警
*/
private function sendEmailAlert($record) {
if (empty($this->config['email_recipients'])) {
return;
}
$subject = sprintf(
'[%s] Log Alert: %s',
$record['level'],
substr($record['message'], 0, 100)
);
$body = print_r($record, true);
foreach ($this->config['email_recipients'] as $recipient) {
@mail($recipient, $subject, $body);
}
}
/**
* 检查日志级别是否允许
*/
private function isLevelAllowed($level) {
return self::LEVELS[$level] >= self::LEVELS[$this->config['log_level']];
}
/**
* 检查是否达到指定级别
*/
private function isLevelReached($current, $target) {
return self::LEVELS[$current] >= self::LEVELS[$target];
}
// ... 其他辅助方法与基础版本相同
private function __clone() {}
private function __wakeup() {}
}
使用示例
<?php
// 配置文件
$config = [
'log_dir' => '/path/to/logs',
'log_level' => 'DEBUG',
'file_prefix' => 'myapp',
'max_file_size' => 5242880, // 5MB
'backup_count' => 3,
'enable_buffer' => true,
'buffer_size' => 50,
'format' => 'json',
'enable_email' => true,
'email_recipients' => ['admin@example.com'],
'email_level' => 'ERROR',
'enable_console' => true
];
// 获取日志实例
$logger = Logger::getInstance($config);
// 基本使用
$logger->info('User logged in', ['user_id' => 123, 'username' => 'john']);
$logger->error('Database connection failed', ['error_code' => 1045]);
$logger->debug('Memory usage: {memory}', ['memory' => memory_get_usage()]);
// 使用高级版本
$advLogger = AdvancedLogger::getInstance($config);
// 批量记录
$advLogger->log('INFO', 'API Request', [
'endpoint' => '/users',
'method' => 'GET',
'params' => $_GET
]);
// 异常记录
try {
// 一些代码
throw new Exception('Something went wrong');
} catch (Exception $e) {
$logger->critical($e->getMessage(), [
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTraceAsString()
]);
}
// 性能监控
$logger->info('Page load completed', [
'execution_time' => (microtime(true) - START_TIME),
'memory_usage' => memory_get_usage()
]);
特点说明
- 单例模式:确保全局只有一个日志实例
- 多级别支持:DEBUG到CRITICAL多个级别
- 自动轮转:当文件超过指定大小时自动分割
- 日期目录:按日期创建子目录,便于管理
- 缓冲机制:支持批量写入,提高性能
- 上下文信息:自动记录调用位置、IP、URI等
- 多种输出:支持文件、控制台、邮件
- 格式化灵活:支持JSON和文本格式
这个日志类封装了企业级应用所需的大部分功能,你可以根据实际需求进行扩展和调整。