workingCopy/ecomzone/classes/EcomZoneLogger.php
2025-06-24 21:46:54 +02:00

178 lines
5.7 KiB
PHP

<?php
if (!defined("_PS_VERSION_")) {
exit();
}
class EcomZoneLogger
{
const LOG_FILE = 'ecomzone.log';
// Log levels with corresponding severity
const LEVELS = [
'DEBUG' => 1,
'INFO' => 2,
'WARNING' => 3,
'ERROR' => 4,
'CRITICAL' => 5
];
// Current log level - can be changed through configuration
private static function getLogLevel()
{
$configLevel = Configuration::get('ECOMZONE_LOG_LEVEL', 'INFO');
return isset(self::LEVELS[$configLevel]) ? $configLevel : 'INFO';
}
public static function log($message, $level = 'INFO', $context = [])
{
try {
// Check if we should log this level
$logLevel = self::getLogLevel();
if (self::LEVELS[$level] < self::LEVELS[$logLevel]) {
return true; // Skip logging for less severe levels
}
// Ensure the log directory exists
$logDir = _PS_MODULE_DIR_ . 'ecomzone/log';
if (!is_dir($logDir)) {
if (!@mkdir($logDir, 0755, true)) {
throw new Exception("Failed to create log directory: " . $logDir);
}
@chmod($logDir, 0755); // Ensure the directory is writable
}
// Format the log entry
$logEntry = [
'timestamp' => date('Y-m-d H:i:s'),
'level' => strtoupper($level),
'message' => $message,
'context' => $context
];
// For errors and critical issues, also include a stack trace
if (in_array($level, ['ERROR', 'CRITICAL']) && !isset($context['trace'])) {
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
$logEntry['trace'] = array_slice($trace, 1); // Skip the log function itself
}
$logLine = json_encode($logEntry) . PHP_EOL;
// Rotate log file if it's too large (> 10MB)
$logFile = $logDir . '/' . self::LOG_FILE;
if (file_exists($logFile) && filesize($logFile) > 10 * 1024 * 1024) {
self::rotateLogFile($logFile);
}
// Write to log file with exclusive lock
if (!@file_put_contents($logFile, $logLine, FILE_APPEND | LOCK_EX)) {
throw new Exception("Failed to write to log file: " . $logFile);
}
// Also write to PrestaShop log for error/critical levels
if (in_array($level, ['ERROR', 'CRITICAL'])) {
$psLogLevel = ($level === 'ERROR') ? 3 : 4;
$contextStr = json_encode($context);
PrestaShopLogger::addLog(
"EcomZone: {$message} - Context: {$contextStr}",
$psLogLevel,
null,
'EcomZone'
);
}
return true;
} catch (Exception $e) {
// Last resort logging to PHP error log
error_log('EcomZone logging error: ' . $e->getMessage() .
'. Original message: ' . $message);
// Try to log to PrestaShop's log
try {
PrestaShopLogger::addLog(
'EcomZone logging error: ' . $e->getMessage() . '. Original message: ' . $message,
3, // Error level
null,
'EcomZone'
);
} catch (Exception $innerEx) {
error_log('Failed to log to PrestaShop: ' . $innerEx->getMessage());
}
return false;
}
}
private static function rotateLogFile($logFile)
{
try {
$backup = $logFile . '.' . date('Y-m-d-H-i-s') . '.bak';
if (!@rename($logFile, $backup)) {
throw new Exception("Failed to rotate log file");
}
// Keep only last 5 backup files
$backups = glob($logFile . '.*.bak');
if (count($backups) > 5) {
usort($backups, function($a, $b) {
return filemtime($b) - filemtime($a);
});
$toDelete = array_slice($backups, 5);
foreach ($toDelete as $file) {
@unlink($file);
}
}
return true;
} catch (Exception $e) {
error_log('EcomZone log rotation error: ' . $e->getMessage());
return false;
}
}
public static function getLogPath()
{
// When running in standalone mode, use the local directory
if (!defined('_PS_MODULE_DIR_') || !file_exists(_PS_MODULE_DIR_ . 'ecomzone')) {
return __DIR__ . '/../log/' . self::LOG_FILE;
}
return _PS_MODULE_DIR_ . 'ecomzone/log/' . self::LOG_FILE;
}
public static function getLogContents($maxLines = 100)
{
$logFile = self::getLogPath();
if (!file_exists($logFile)) {
return [];
}
$logs = [];
$lines = array_reverse(file($logFile));
$count = 0;
foreach ($lines as $line) {
if ($count >= $maxLines) break;
$entry = json_decode($line, true);
if ($entry) {
$logs[] = $entry;
$count++;
}
}
return $logs;
}
public static function clearLogs()
{
$logFile = self::getLogPath();
if (file_exists($logFile)) {
@unlink($logFile);
self::log("Log file cleared", "INFO");
}
return true;
}
}