Make your WordPress site irresistible. Natural SEO attraction with: - robots.txt management - sitemap.xml generation - LLMs.txt support - Google integration (Analytics, Search Console, Tag Manager) - Schema.org structured data - Open Graph / Twitter Card meta tags - AMP support - Visual elements gallery - Built-in backup/restore module Includes build.sh and .distignore for WordPress-installable release ZIPs.
575 lines
16 KiB
PHP
575 lines
16 KiB
PHP
<?php
|
|
/**
|
|
* Enterprise Backup Logger for TigerStyle SEO
|
|
* Multi-level logging with rotation and export capabilities
|
|
*/
|
|
|
|
// Prevent direct access
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
class TigerStyleSEO_Backup_Logger {
|
|
|
|
/**
|
|
* Single instance
|
|
*/
|
|
private static $instance = null;
|
|
|
|
/**
|
|
* Log levels
|
|
*/
|
|
const LEVEL_DEBUG = 1;
|
|
const LEVEL_INFO = 2;
|
|
const LEVEL_WARNING = 3;
|
|
const LEVEL_ERROR = 4;
|
|
const LEVEL_CRITICAL = 5;
|
|
|
|
/**
|
|
* Log level names
|
|
*/
|
|
private $level_names = array(
|
|
self::LEVEL_DEBUG => 'DEBUG',
|
|
self::LEVEL_INFO => 'INFO',
|
|
self::LEVEL_WARNING => 'WARNING',
|
|
self::LEVEL_ERROR => 'ERROR',
|
|
self::LEVEL_CRITICAL => 'CRITICAL'
|
|
);
|
|
|
|
/**
|
|
* Log settings
|
|
*/
|
|
private $settings = array();
|
|
|
|
/**
|
|
* Get instance
|
|
*/
|
|
public static function instance() {
|
|
if (is_null(self::$instance)) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
private function __construct() {
|
|
$this->init();
|
|
}
|
|
|
|
/**
|
|
* Initialize logger
|
|
*/
|
|
private function init() {
|
|
$this->settings = array(
|
|
'enabled' => get_option('backup_logging_enabled', true),
|
|
'level' => get_option('backup_logging_level', self::LEVEL_INFO),
|
|
'file_enabled' => get_option('backup_logging_file_enabled', true),
|
|
'database_enabled' => false, // Disabled to avoid complex database setup
|
|
'email_enabled' => get_option('backup_logging_email_enabled', false),
|
|
'wordpress_debug_enabled' => get_option('backup_logging_wp_debug_enabled', true), // Use WordPress debug instead
|
|
'log_file' => WP_CONTENT_DIR . '/tigerstyle-backup-logs/backup.log',
|
|
'max_file_size' => 10485760, // 10MB
|
|
'max_log_files' => 5,
|
|
'email_level' => self::LEVEL_ERROR,
|
|
'email_recipient' => get_option('admin_email')
|
|
);
|
|
|
|
// Ensure log directory exists
|
|
$log_dir = dirname($this->settings['log_file']);
|
|
if (!is_dir($log_dir)) {
|
|
wp_mkdir_p($log_dir);
|
|
|
|
// Protect log directory
|
|
$htaccess_content = "Order deny,allow\nDeny from all\n";
|
|
file_put_contents($log_dir . '/.htaccess', $htaccess_content);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Log debug message
|
|
*/
|
|
public function debug($message, $context = array()) {
|
|
$this->log(self::LEVEL_DEBUG, $message, $context);
|
|
}
|
|
|
|
/**
|
|
* Log info message
|
|
*/
|
|
public function info($message, $context = array()) {
|
|
$this->log(self::LEVEL_INFO, $message, $context);
|
|
}
|
|
|
|
/**
|
|
* Log warning message
|
|
*/
|
|
public function warning($message, $context = array()) {
|
|
$this->log(self::LEVEL_WARNING, $message, $context);
|
|
}
|
|
|
|
/**
|
|
* Log error message
|
|
*/
|
|
public function error($message, $context = array()) {
|
|
$this->log(self::LEVEL_ERROR, $message, $context);
|
|
}
|
|
|
|
/**
|
|
* Log critical message
|
|
*/
|
|
public function critical($message, $context = array()) {
|
|
$this->log(self::LEVEL_CRITICAL, $message, $context);
|
|
}
|
|
|
|
/**
|
|
* Main logging method
|
|
*/
|
|
private function log($level, $message, $context = array()) {
|
|
if (!$this->settings['enabled'] || $level < $this->settings['level']) {
|
|
return;
|
|
}
|
|
|
|
$log_entry = $this->create_log_entry($level, $message, $context);
|
|
|
|
// Log to file
|
|
if ($this->settings['file_enabled']) {
|
|
$this->log_to_file($log_entry);
|
|
}
|
|
|
|
// Log to database
|
|
if ($this->settings['database_enabled']) {
|
|
$this->log_to_database($level, $message, $context);
|
|
}
|
|
|
|
// Log to WordPress debug
|
|
if ($this->settings['wordpress_debug_enabled'] && WP_DEBUG_LOG) {
|
|
$this->log_to_wordpress_debug($log_entry);
|
|
}
|
|
|
|
// Send email for critical errors
|
|
if ($this->settings['email_enabled'] && $level >= $this->settings['email_level']) {
|
|
$this->send_email_notification($level, $message, $context);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create formatted log entry
|
|
*/
|
|
private function create_log_entry($level, $message, $context) {
|
|
$timestamp = current_time('Y-m-d H:i:s');
|
|
$level_name = $this->level_names[$level];
|
|
$user_info = $this->get_user_info();
|
|
$memory_usage = $this->format_bytes(memory_get_usage(true));
|
|
|
|
$log_data = array(
|
|
'timestamp' => $timestamp,
|
|
'level' => $level_name,
|
|
'message' => $message,
|
|
'context' => $context,
|
|
'user' => $user_info,
|
|
'memory' => $memory_usage,
|
|
'request_id' => $this->get_request_id()
|
|
);
|
|
|
|
return $log_data;
|
|
}
|
|
|
|
/**
|
|
* Log to file
|
|
*/
|
|
private function log_to_file($log_entry) {
|
|
// Check if log rotation is needed
|
|
if (file_exists($this->settings['log_file']) && filesize($this->settings['log_file']) > $this->settings['max_file_size']) {
|
|
$this->rotate_log_file();
|
|
}
|
|
|
|
$formatted_entry = $this->format_log_entry_for_file($log_entry);
|
|
|
|
// Append to log file
|
|
file_put_contents($this->settings['log_file'], $formatted_entry . "\n", FILE_APPEND | LOCK_EX);
|
|
}
|
|
|
|
/**
|
|
* Log to database
|
|
*/
|
|
private function log_to_database($level, $message, $context) {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'tigerstyle_backup_logs';
|
|
|
|
$wpdb->insert(
|
|
$table_name,
|
|
array(
|
|
'created_at' => current_time('mysql'),
|
|
'level' => $this->level_names[$level],
|
|
'message' => $message,
|
|
'context' => json_encode($context),
|
|
'user_id' => get_current_user_id(),
|
|
'user_ip' => $this->get_user_ip(),
|
|
'memory_usage' => memory_get_usage(true),
|
|
'request_id' => $this->get_request_id()
|
|
),
|
|
array('%s', '%s', '%s', '%s', '%d', '%s', '%d', '%s')
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Log to WordPress debug
|
|
*/
|
|
private function log_to_wordpress_debug($log_entry) {
|
|
$formatted_entry = sprintf(
|
|
'[TigerStyle Backup] [%s] %s %s',
|
|
$log_entry['level'],
|
|
$log_entry['message'],
|
|
!empty($log_entry['context']) ? json_encode($log_entry['context']) : ''
|
|
);
|
|
|
|
error_log($formatted_entry);
|
|
}
|
|
|
|
/**
|
|
* Send email notification
|
|
*/
|
|
private function send_email_notification($level, $message, $context) {
|
|
if (!$this->settings['email_recipient']) {
|
|
return;
|
|
}
|
|
|
|
$subject = sprintf(
|
|
'[%s] TigerStyle SEO Backup %s: %s',
|
|
get_bloginfo('name'),
|
|
$this->level_names[$level],
|
|
$message
|
|
);
|
|
|
|
$body = "A backup system event occurred:\n\n";
|
|
$body .= "Level: " . $this->level_names[$level] . "\n";
|
|
$body .= "Message: " . $message . "\n";
|
|
$body .= "Time: " . current_time('Y-m-d H:i:s') . "\n";
|
|
$body .= "Site: " . site_url() . "\n";
|
|
|
|
if (!empty($context)) {
|
|
$body .= "\nContext:\n" . print_r($context, true);
|
|
}
|
|
|
|
$body .= "\n---\nThis is an automated message from TigerStyle SEO Backup System.";
|
|
|
|
wp_mail($this->settings['email_recipient'], $subject, $body);
|
|
}
|
|
|
|
/**
|
|
* Format log entry for file output
|
|
*/
|
|
private function format_log_entry_for_file($log_entry) {
|
|
$context_str = !empty($log_entry['context']) ? json_encode($log_entry['context']) : '';
|
|
|
|
return sprintf(
|
|
'[%s] [%s] [%s] [MEM:%s] [REQ:%s] %s %s',
|
|
$log_entry['timestamp'],
|
|
$log_entry['level'],
|
|
$log_entry['user']['display'],
|
|
$log_entry['memory'],
|
|
$log_entry['request_id'],
|
|
$log_entry['message'],
|
|
$context_str
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Rotate log file
|
|
*/
|
|
private function rotate_log_file() {
|
|
$base_file = $this->settings['log_file'];
|
|
|
|
// Remove oldest log file
|
|
$oldest_log = $base_file . '.' . $this->settings['max_log_files'];
|
|
if (file_exists($oldest_log)) {
|
|
unlink($oldest_log);
|
|
}
|
|
|
|
// Rotate existing log files
|
|
for ($i = $this->settings['max_log_files'] - 1; $i >= 1; $i--) {
|
|
$current_log = $base_file . '.' . $i;
|
|
$next_log = $base_file . '.' . ($i + 1);
|
|
|
|
if (file_exists($current_log)) {
|
|
rename($current_log, $next_log);
|
|
}
|
|
}
|
|
|
|
// Move current log to .1
|
|
if (file_exists($base_file)) {
|
|
rename($base_file, $base_file . '.1');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get user information
|
|
*/
|
|
private function get_user_info() {
|
|
$user = wp_get_current_user();
|
|
|
|
if ($user->ID) {
|
|
return array(
|
|
'id' => $user->ID,
|
|
'login' => $user->user_login,
|
|
'display' => $user->display_name,
|
|
'email' => $user->user_email
|
|
);
|
|
} else {
|
|
return array(
|
|
'id' => 0,
|
|
'login' => 'guest',
|
|
'display' => 'Guest User',
|
|
'email' => ''
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get user IP address
|
|
*/
|
|
private function get_user_ip() {
|
|
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
|
return sanitize_text_field($_SERVER['HTTP_X_FORWARDED_FOR']);
|
|
} elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) {
|
|
return sanitize_text_field($_SERVER['HTTP_X_REAL_IP']);
|
|
} elseif (!empty($_SERVER['REMOTE_ADDR'])) {
|
|
return sanitize_text_field($_SERVER['REMOTE_ADDR']);
|
|
}
|
|
|
|
return 'unknown';
|
|
}
|
|
|
|
/**
|
|
* Get request ID for tracking related log entries
|
|
*/
|
|
private function get_request_id() {
|
|
static $request_id = null;
|
|
|
|
if ($request_id === null) {
|
|
$request_id = substr(md5(uniqid(mt_rand(), true)), 0, 8);
|
|
}
|
|
|
|
return $request_id;
|
|
}
|
|
|
|
/**
|
|
* Format bytes for display
|
|
*/
|
|
private function format_bytes($bytes, $precision = 2) {
|
|
$units = array('B', 'KB', 'MB', 'GB', 'TB');
|
|
|
|
for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
|
|
$bytes /= 1024;
|
|
}
|
|
|
|
return round($bytes, $precision) . $units[$i];
|
|
}
|
|
|
|
/**
|
|
* Get recent log entries
|
|
*/
|
|
public function get_recent_logs($limit = 100, $level_filter = null) {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'tigerstyle_backup_logs';
|
|
|
|
$sql = "SELECT * FROM {$table_name}";
|
|
$where_conditions = array();
|
|
$where_values = array();
|
|
|
|
if ($level_filter && isset($this->level_names[$level_filter])) {
|
|
$where_conditions[] = "level = %s";
|
|
$where_values[] = $this->level_names[$level_filter];
|
|
}
|
|
|
|
if (!empty($where_conditions)) {
|
|
$sql .= " WHERE " . implode(' AND ', $where_conditions);
|
|
}
|
|
|
|
$sql .= " ORDER BY created_at DESC LIMIT %d";
|
|
$where_values[] = $limit;
|
|
|
|
if (!empty($where_values)) {
|
|
$sql = $wpdb->prepare($sql, $where_values);
|
|
}
|
|
|
|
return $wpdb->get_results($sql, ARRAY_A);
|
|
}
|
|
|
|
/**
|
|
* Export logs to file
|
|
*/
|
|
public function export_logs($format = 'json', $date_from = null, $date_to = null) {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'tigerstyle_backup_logs';
|
|
|
|
$sql = "SELECT * FROM {$table_name}";
|
|
$where_conditions = array();
|
|
$where_values = array();
|
|
|
|
if ($date_from) {
|
|
$where_conditions[] = "created_at >= %s";
|
|
$where_values[] = $date_from;
|
|
}
|
|
|
|
if ($date_to) {
|
|
$where_conditions[] = "created_at <= %s";
|
|
$where_values[] = $date_to;
|
|
}
|
|
|
|
if (!empty($where_conditions)) {
|
|
$sql .= " WHERE " . implode(' AND ', $where_conditions);
|
|
}
|
|
|
|
$sql .= " ORDER BY created_at DESC";
|
|
|
|
if (!empty($where_values)) {
|
|
$sql = $wpdb->prepare($sql, $where_values);
|
|
}
|
|
|
|
$logs = $wpdb->get_results($sql, ARRAY_A);
|
|
|
|
switch ($format) {
|
|
case 'json':
|
|
return json_encode($logs, JSON_PRETTY_PRINT);
|
|
case 'csv':
|
|
return $this->logs_to_csv($logs);
|
|
case 'txt':
|
|
return $this->logs_to_text($logs);
|
|
default:
|
|
return $logs;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert logs to CSV format
|
|
*/
|
|
private function logs_to_csv($logs) {
|
|
if (empty($logs)) {
|
|
return '';
|
|
}
|
|
|
|
$csv = "Timestamp,Level,Message,User ID,User IP,Memory Usage,Request ID\n";
|
|
|
|
foreach ($logs as $log) {
|
|
$csv .= sprintf(
|
|
'"%s","%s","%s","%s","%s","%s","%s"' . "\n",
|
|
$log['created_at'],
|
|
$log['level'],
|
|
str_replace('"', '""', $log['message']),
|
|
$log['user_id'],
|
|
$log['user_ip'],
|
|
$log['memory_usage'],
|
|
$log['request_id']
|
|
);
|
|
}
|
|
|
|
return $csv;
|
|
}
|
|
|
|
/**
|
|
* Convert logs to text format
|
|
*/
|
|
private function logs_to_text($logs) {
|
|
if (empty($logs)) {
|
|
return '';
|
|
}
|
|
|
|
$text = "TigerStyle SEO Backup Logs Export\n";
|
|
$text .= "Generated: " . current_time('Y-m-d H:i:s') . "\n";
|
|
$text .= str_repeat('=', 50) . "\n\n";
|
|
|
|
foreach ($logs as $log) {
|
|
$text .= sprintf(
|
|
"[%s] [%s] %s\n",
|
|
$log['created_at'],
|
|
$log['level'],
|
|
$log['message']
|
|
);
|
|
|
|
if (!empty($log['context'])) {
|
|
$context = json_decode($log['context'], true);
|
|
if ($context) {
|
|
$text .= " Context: " . print_r($context, true) . "\n";
|
|
}
|
|
}
|
|
|
|
$text .= "\n";
|
|
}
|
|
|
|
return $text;
|
|
}
|
|
|
|
/**
|
|
* Clear old logs
|
|
*/
|
|
public function cleanup_old_logs($days = 30) {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'tigerstyle_backup_logs';
|
|
$cutoff_date = date('Y-m-d H:i:s', strtotime("-{$days} days"));
|
|
|
|
$deleted = $wpdb->query(
|
|
$wpdb->prepare(
|
|
"DELETE FROM {$table_name} WHERE created_at < %s",
|
|
$cutoff_date
|
|
)
|
|
);
|
|
|
|
$this->info("Cleaned up old log entries", array(
|
|
'deleted_count' => $deleted,
|
|
'cutoff_date' => $cutoff_date
|
|
));
|
|
|
|
return $deleted;
|
|
}
|
|
|
|
/**
|
|
* Get log statistics
|
|
*/
|
|
public function get_log_statistics($days = 7) {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'tigerstyle_backup_logs';
|
|
$cutoff_date = date('Y-m-d H:i:s', strtotime("-{$days} days"));
|
|
|
|
$stats = array();
|
|
|
|
// Total logs
|
|
$stats['total'] = $wpdb->get_var(
|
|
$wpdb->prepare(
|
|
"SELECT COUNT(*) FROM {$table_name} WHERE created_at >= %s",
|
|
$cutoff_date
|
|
)
|
|
);
|
|
|
|
// Logs by level
|
|
$level_stats = $wpdb->get_results(
|
|
$wpdb->prepare(
|
|
"SELECT level, COUNT(*) as count FROM {$table_name} WHERE created_at >= %s GROUP BY level",
|
|
$cutoff_date
|
|
),
|
|
ARRAY_A
|
|
);
|
|
|
|
$stats['by_level'] = array();
|
|
foreach ($level_stats as $level_stat) {
|
|
$stats['by_level'][$level_stat['level']] = $level_stat['count'];
|
|
}
|
|
|
|
// Recent errors
|
|
$stats['recent_errors'] = $wpdb->get_results(
|
|
$wpdb->prepare(
|
|
"SELECT * FROM {$table_name} WHERE level IN ('ERROR', 'CRITICAL') AND created_at >= %s ORDER BY created_at DESC LIMIT 10",
|
|
$cutoff_date
|
|
),
|
|
ARRAY_A
|
|
);
|
|
|
|
return $stats;
|
|
}
|
|
} |