tigerstyle-heat/includes/backup/class-backup-logger.php
Ryan Malloy 0028738e33 Initial commit: TigerStyle Heat v2.0.0
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.
2026-05-27 13:41:35 -06:00

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;
}
}