Files
wp-multisite-waas/inc/class-logger.php
2025-02-07 19:02:33 -07:00

293 lines
6.4 KiB
PHP

<?php
/**
* WP Multisite WaaS Logger
*
* Log string messages to a file with a timestamp. Useful for debugging.
*
* @package WP_Ultimo
* @subpackage Logger
* @since 2.0.0
*/
namespace WP_Ultimo;
// Exit if accessed directly
defined('ABSPATH') || exit;
use Psr\Log\AbstractLogger;
use Psr\Log\LogLevel;
/**
* WP Multisite WaaS Logger
*
* @since 2.0.0
*/
class Logger extends AbstractLogger {
use \WP_Ultimo\Traits\Singleton;
/**
* Holds the log file path.
*
* @since 2.1
* @var string
*/
protected $log_file = '';
/**
* Returns the logs folder
*
* @return string
*/
public static function get_logs_folder() {
return wu_maybe_create_folder('wu-logs');
}
/**
* Add a log entry to chosen file.
*
* @param string $handle Name of the log file to write to.
* @param string|\WP_Error $message Log message to write.
* @param string $log_level Log level to write.
*/
public static function add($handle, $message, $log_level = LogLevel::INFO) {
$allowed_log_level = wu_get_setting('error_logging_level', 'default');
if ($allowed_log_level === 'disabled') {
return;
}
if ($allowed_log_level === 'default') {
/**
* Get from default php reporting level
*
* Here we are converting the PHP error reporting level to the PSR-3 log level.
*/
$reporting_level = error_reporting();
$psr_log_levels = array(
E_ERROR => LogLevel::ERROR,
E_WARNING => LogLevel::WARNING,
E_PARSE => LogLevel::ERROR,
E_NOTICE => LogLevel::NOTICE,
E_CORE_ERROR => LogLevel::CRITICAL,
E_CORE_WARNING => LogLevel::WARNING,
E_COMPILE_ERROR => LogLevel::ALERT,
E_COMPILE_WARNING => LogLevel::WARNING,
E_USER_ERROR => LogLevel::ERROR,
E_USER_WARNING => LogLevel::WARNING,
E_USER_NOTICE => LogLevel::NOTICE,
E_STRICT => LogLevel::DEBUG,
E_RECOVERABLE_ERROR => LogLevel::ERROR,
E_DEPRECATED => LogLevel::NOTICE,
E_USER_DEPRECATED => LogLevel::NOTICE,
);
$current_log_levels = array();
foreach ($psr_log_levels as $php_level => $psr_level) {
if ($reporting_level & $php_level) {
$current_log_levels[] = $psr_level;
}
}
if ( ! in_array($log_level, $current_log_levels, true) && ($reporting_level & ~E_ALL)) {
return;
}
} elseif ($allowed_log_level === 'errors' && $log_level !== LogLevel::ERROR && $log_level !== LogLevel::CRITICAL) {
return;
}
$instance = self::get_instance();
$instance->set_log_file(self::get_logs_folder() . "/$handle.log");
if (is_wp_error($message)) {
$message = $message->get_error_message();
}
$instance->log($log_level, $message);
do_action('wu_log_add', $handle, $message);
}
/**
* Get the log contents
*
* @since 1.6.0
*
* @param string $handle File name to read.
* @param integer $lines Number of lines to retrieve, defaults to 10.
* @return array
*/
public static function read_lines($handle, $lines = 10) {
$file = self::get_logs_folder() . "/$handle.log";
if ( ! file_exists($file)) {
return array();
}
// read file
$content = file_get_contents($file);
// split into lines
$arr_content = explode(PHP_EOL, $content);
// remove last line if empty
if (empty(end($arr_content))) {
array_pop($arr_content);
}
// return last lines
return array_slice($arr_content, -$lines);
}
/**
* Clear entries from chosen file.
*
* @param mixed $handle Name of the log file to clear.
*/
public static function clear($handle) {
$file = self::get_logs_folder() . "/$handle.log";
// Delete the file if it exists.
if (file_exists($file)) {
@unlink($file); // phpcs:ignore
}
do_action('wu_log_clear', $handle);
}
/**
* Takes a callable as a parameter and logs how much time it took to execute it.
*
* @since 2.0.0
*
* @param string $handle Name of the log file to write to.
* @param string $message Log message to write.
* @param callable $callback Function to track the execution time.
* @return array
*/
public static function track_time($handle, $message, $callback) {
$start = microtime(true);
$return = call_user_func($callback);
$time_elapsed = microtime(true) - $start;
// translators: the placeholder %s will be replaced by the time in seconds (float).
$message .= ' - ' . sprintf(__('This action took %s seconds.', 'wp-ultimo'), $time_elapsed);
self::add($handle, $message);
return $return;
}
/**
* Set the log file path.
*
* @since 2.1
*
* @param string $log_file The log file path.
*/
public function set_log_file($log_file) {
$this->log_file = $log_file;
}
/**
* Logs with an arbitrary level.
*
* @since 2.1
*
* @param mixed $level The log level.
* @param string $message The message to log.
* @param mixed[] $context The context.
*
* @return void
*/
public function log($level, $message, array $context = array()) {
if ( ! $this->is_valid_log_level($level) ) {
return;
}
$formatted_message = $this->format_message($level, $message, $context);
$this->write_to_file($formatted_message);
}
/**
* Check if the log level is valid.
*
* @since 2.1
*
* @param string $level The log level to check.
*/
protected function is_valid_log_level($level): bool {
$valid_log_levels = array(
LogLevel::EMERGENCY,
LogLevel::ALERT,
LogLevel::CRITICAL,
LogLevel::ERROR,
LogLevel::WARNING,
LogLevel::NOTICE,
LogLevel::INFO,
LogLevel::DEBUG,
);
return in_array($level, $valid_log_levels, true);
}
/**
* Format the message to be logged.
*
* @since 2.1
*
* @param string $level The log level.
* @param string $message The message to log.
* @param array $context The context of the message.
* @return string
*/
protected function format_message($level, $message, $context = array()) {
$date = new \DateTime();
$formatted_message = sprintf(
'[%s] [%s] %s' . PHP_EOL,
$date->format('Y-m-d H:i:s'),
strtoupper($level),
$message
);
return $formatted_message;
}
/**
* Write the message to the log file.
*
* @since 2.1
*
* @param string $message The message to log.
* @return void
*/
protected function write_to_file($message) {
if ( ! file_exists($this->log_file)) {
touch($this->log_file);
}
if ( ! is_writable($this->log_file)) {
return;
}
file_put_contents($this->log_file, $message, FILE_APPEND | LOCK_EX);
}
}