AlkantarClanX12
| Current Path : /www/capitalgmcbuickregina_830/public/wp-content/plugins/leadbox/lib/ |
| Current File : /www/capitalgmcbuickregina_830/public/wp-content/plugins/leadbox/lib/class-logger.php |
<?php
/**
* Leadbox Logger
*
* PSR-3 compliant logging with structured context
*
* @package Leadbox
* @since 6.5.0
*/
use Psr\Log\AbstractLogger;
use Psr\Log\LogLevel;
/**
* Leadbox Logger Class
*
* Provides structured logging with automatic context injection
*/
class LB_Logger extends AbstractLogger
{
/**
* Minimum log level to record
*
* @var string
*/
private $min_level;
/**
* Log level hierarchy
*
* @var array
*/
private static $levels = array(
LogLevel::DEBUG => 100,
LogLevel::INFO => 200,
LogLevel::NOTICE => 250,
LogLevel::WARNING => 300,
LogLevel::ERROR => 400,
LogLevel::CRITICAL => 500,
LogLevel::ALERT => 550,
LogLevel::EMERGENCY => 600,
);
/**
* Singleton instance
*
* @var LB_Logger
*/
private static $instance = null;
/**
* Request ID for this request
*
* @var string
*/
private $request_id;
/**
* Private constructor (singleton pattern)
*/
private function __construct()
{
// Set minimum log level from wp-config or default to ERROR (production-safe)
$this->min_level = defined('LEADBOX_LOG_LEVEL')
? LEADBOX_LOG_LEVEL
: LogLevel::ERROR;
// Generate request ID
$this->request_id = $this->generate_request_id();
}
/**
* Get singleton instance
*
* @return LB_Logger
*/
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Log a message
*
* @param string $level Log level
* @param string $message Log message
* @param array $context Additional context
*/
public function log($level, $message, array $context = array())
{
// Check if this level should be logged
if (!$this->should_log($level)) {
return;
}
// Build log entry
$entry = $this->build_log_entry($level, $message, $context);
// Write to file (only if WP_DEBUG_LOG enabled)
$this->write_to_file($entry);
// Database logging disabled by default (opt-in only)
if (defined('LEADBOX_LOG_TO_DATABASE') && LEADBOX_LOG_TO_DATABASE) {
$this->write_to_database($entry);
}
// Allow external monitoring services
do_action('leadbox_log_entry', $entry);
}
/**
* Check if log level should be recorded
*
* @param string $level Log level
* @return bool
*/
private function should_log($level)
{
$level_value = self::$levels[$level] ?? 0;
$min_value = self::$levels[$this->min_level] ?? 0;
return $level_value >= $min_value;
}
/**
* Build structured log entry
*
* @param string $level Log level
* @param string $message Log message
* @param array $context Additional context
* @return array
*/
private function build_log_entry($level, $message, array $context)
{
// Interpolate context variables into message
$message = $this->interpolate($message, $context);
// Build entry
$entry = array(
'timestamp' => current_time('mysql'),
'timestamp_unix' => time(),
'level' => strtoupper($level),
'message' => $message,
'request_id' => $this->request_id,
'context' => $context,
);
// Add automatic context
$entry['user_id'] = get_current_user_id();
$entry['ip_address'] = $this->get_ip_address();
$entry['url'] = $this->get_current_url();
$entry['user_agent'] = isset($_SERVER['HTTP_USER_AGENT'])
? sanitize_text_field($_SERVER['HTTP_USER_AGENT'])
: null;
// Add memory and performance data for errors
if (in_array($level, array(LogLevel::ERROR, LogLevel::CRITICAL, LogLevel::EMERGENCY))) {
$entry['memory_usage'] = memory_get_usage(true);
$entry['memory_peak'] = memory_get_peak_usage(true);
}
return $entry;
}
/**
* Interpolate context values into message
*
* @param string $message Message with placeholders
* @param array $context Context values
* @return string
*/
private function interpolate($message, array $context)
{
$replace = array();
foreach ($context as $key => $val) {
if (is_scalar($val) || (is_object($val) && method_exists($val, '__toString'))) {
$replace['{' . $key . '}'] = $val;
}
}
return strtr($message, $replace);
}
/**
* Write log entry to file
*
* @param array $entry Log entry
*/
private function write_to_file(array $entry)
{
// Use WordPress debug log if enabled
if (!defined('WP_DEBUG_LOG') || !WP_DEBUG_LOG) {
return;
}
$log_line = sprintf(
"[%s] %s: %s (Request: %s, User: %d)\n",
$entry['timestamp'],
$entry['level'],
$entry['message'],
$entry['request_id'],
$entry['user_id']
);
// Add context if present
if (!empty($entry['context'])) {
$log_line .= 'Context: ' . wp_json_encode($entry['context']) . "\n";
}
// Write to separate Leadbox log file
$log_file = WP_CONTENT_DIR . '/leadbox-debug.log';
// Append to file (creates file if doesn't exist)
file_put_contents($log_file, $log_line, FILE_APPEND | LOCK_EX);
}
/**
* Write log entry to database
*
* @param array $entry Log entry
*/
private function write_to_database(array $entry)
{
global $wpdb;
// Only log errors and above to database
$level_value = self::$levels[$entry['level']] ?? 0;
if ($level_value < self::$levels[LogLevel::ERROR]) {
return;
}
// Table name
$table = $wpdb->prefix . 'leadbox_logs';
// Check if table exists (create on first use)
if ($wpdb->get_var("SHOW TABLES LIKE '$table'") !== $table) {
$this->create_log_table();
}
// Insert log entry
$wpdb->insert(
$table,
array(
'timestamp' => $entry['timestamp'],
'level' => $entry['level'],
'message' => $entry['message'],
'request_id' => $entry['request_id'],
'user_id' => $entry['user_id'],
'ip_address' => $entry['ip_address'],
'url' => $entry['url'],
'context' => wp_json_encode($entry['context']),
'memory_usage' => $entry['memory_usage'] ?? null,
),
array('%s', '%s', '%s', '%s', '%d', '%s', '%s', '%s', '%d')
);
}
/**
* Create log table
*/
private function create_log_table()
{
global $wpdb;
$table = $wpdb->prefix . 'leadbox_logs';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
timestamp datetime NOT NULL,
level varchar(20) NOT NULL,
message text NOT NULL,
request_id varchar(40) NOT NULL,
user_id bigint(20) unsigned DEFAULT NULL,
ip_address varchar(45) DEFAULT NULL,
url text DEFAULT NULL,
context longtext DEFAULT NULL,
memory_usage bigint(20) unsigned DEFAULT NULL,
PRIMARY KEY (id),
KEY level (level),
KEY timestamp (timestamp),
KEY request_id (request_id),
KEY user_id (user_id)
) $charset_collate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($sql);
}
/**
* Generate unique request ID
*
* @return string
*/
private function generate_request_id()
{
return substr(md5(uniqid('', true)), 0, 16);
}
/**
* Get client IP address
*
* @return string|null
*/
private function get_ip_address()
{
$headers = array(
'HTTP_CF_CONNECTING_IP',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_REAL_IP',
'REMOTE_ADDR',
);
foreach ($headers as $header) {
if (!empty($_SERVER[$header])) {
$ip = sanitize_text_field($_SERVER[$header]);
// Take first IP if multiple
if (strpos($ip, ',') !== false) {
$ip = trim(explode(',', $ip)[0]);
}
return $ip;
}
}
return null;
}
/**
* Get current URL
*
* @return string|null
*/
private function get_current_url()
{
if (!isset($_SERVER['REQUEST_URI'])) {
return null;
}
$protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http';
$host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field($_SERVER['HTTP_HOST']) : '';
$uri = sanitize_text_field($_SERVER['REQUEST_URI']);
return $protocol . '://' . $host . $uri;
}
}
/**
* Get logger instance (convenience function)
*
* @return LB_Logger
*/
function leadbox_logger()
{
return LB_Logger::getInstance();
}