Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 9 additions & 13 deletions includes/Admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -290,23 +290,19 @@ function commonsbooking_sanitizeArrayorString( $data, $sanitizeFunction = 'sanit
* @return void
*/
function commonsbooking_write_log( $log, $backtrace = true ) {
$logmessage = is_array( $log ) || is_object( $log ) ? print_r( $log, true ) : (string) $log;

if ( ! WP_DEBUG_LOG ) {
return;
if ( $backtrace ) {
$bt = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );
$logmessage = ( $bt[0]['file'] ?? '' ) . ':' . ( $bt[0]['line'] ?? 0 ) . ' ' . $logmessage;
}

if ( is_array( $log ) || is_object( $log ) ) {
$logmessage = ( print_r( $log, true ) );
} else {
$logmessage = $log;
}
// Always persist to the error monitor — works even without WP_DEBUG_LOG
\CommonsBooking\Service\ErrorMonitor::record( $logmessage, \CommonsBooking\Service\ErrorMonitor::SEVERITY_WARNING, [], 1 );

if ( $backtrace ) {
$bt = debug_backtrace();
$file = $bt[0]['file'];
$line = $bt[0]['line'];
$logmessage = $file . ':' . $line . ' ' . $logmessage;
if ( ! WP_DEBUG_LOG ) {
return;
}

error_log( $logmessage );
error_log( $logmessage ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
}
6 changes: 2 additions & 4 deletions src/API/BaseRoute.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,11 @@ public function validateData( $data ) {
}
}
} catch ( Exception $e ) {
// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_error_log
commonsbooking_write_log( 'Problem while trying to access wp rest endpoint url for schema ' . $this->schemaUrl, false );
commonsbooking_write_log( (string) $e, false );
if ( WP_DEBUG ) {
error_log( 'Problem while trying to access wp rest endpoint url for schema ' . $this->schemaUrl );
error_log( $e );
die;
}
// phpcs:enable
}
}

Expand Down
4 changes: 1 addition & 3 deletions src/API/GBFS/BaseRoute.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ public function getItemData( $request ): array {
$itemdata = $this->prepare_item_for_response( $location, $request );
$data[] = $itemdata->data;
} catch ( Exception $exception ) {
if ( WP_DEBUG ) {
error_log( $exception->getMessage() );
}
commonsbooking_write_log( $exception->getMessage(), false );
}
}

Expand Down
4 changes: 1 addition & 3 deletions src/API/LocationsRoute.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,7 @@ public function getItemData( $request ) {
$itemdata = $this->prepare_item_for_response( $location, $request );
$features[] = $itemdata->get_data();
} catch ( Exception $exception ) {
if ( WP_DEBUG ) {
error_log( $exception->getMessage() );
}
commonsbooking_write_log( $exception->getMessage(), false );
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Helper/NominatimGeoCodeService.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public function getAddressData( $addressString ): ?Location {
return $addresses->first();
}
} catch ( Exception $exception ) {
error_log( $exception->getMessage() );
commonsbooking_write_log( $exception->getMessage(), false );
}

return null;
Expand Down
33 changes: 33 additions & 0 deletions src/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
use CommonsBooking\Service\Upgrade;
use CommonsBooking\Settings\Settings;
use CommonsBooking\Repository\BookingCodes;
use CommonsBooking\Service\ErrorMonitor;
use CommonsBooking\Service\TroubleshootingReport;
use CommonsBooking\View\Dashboard;
use CommonsBooking\View\MassOperations;
use CommonsBooking\View\SystemHealth;
use CommonsBooking\Wordpress\CustomPostType\CustomPostType;
use CommonsBooking\Wordpress\CustomPostType\Item;
use CommonsBooking\Wordpress\CustomPostType\Location;
Expand Down Expand Up @@ -323,6 +326,13 @@ protected static function addRoleCaps( $postType, $roleName ) {
}

public static function admin_init() {
// Handle "clear error log" form submission from System Health page
if ( isset( $_POST['cb_clear_error_log'] ) && check_admin_referer( 'cb_clear_error_log' ) ) {
ErrorMonitor::clear();
wp_safe_redirect( admin_url( 'admin.php?page=cb-system-health&cleared=1' ) );
exit;
}

// check if we have a new version and run tasks
Upgrade::runTasksAfterUpdate();

Expand Down Expand Up @@ -408,6 +418,16 @@ public static function addMenuPages() {
array( MassOperations::class, 'index' )
);
}

// System Health — available to all CB users (not just admins)
add_submenu_page(
'cb-dashboard',
esc_html__( 'System Health', 'commonsbooking' ),
esc_html__( 'System Health', 'commonsbooking' ),
'manage_' . COMMONSBOOKING_PLUGIN_SLUG,
'cb-system-health',
array( SystemHealth::class, 'index' )
);
}

/**
Expand All @@ -425,6 +445,11 @@ public static function handleBookingForms(): void {
$e->getMessage(),
30 // Expires very quickly, so that outdated messsages will not be shown to the user
);
ErrorMonitor::record(
$e->getMessage(),
ErrorMonitor::SEVERITY_WARNING,
[ 'type' => 'BookingDeniedException' ]
);
$targetUrl = $e->getRedirectUrl();
if ( $targetUrl ) {
header( 'Location: ' . $targetUrl );
Expand Down Expand Up @@ -507,6 +532,11 @@ public static function renderError() {

foreach ( $errorTypes as $errorType ) {
if ( $error = get_transient( $errorType ) ) {
ErrorMonitor::record(
$error,
ErrorMonitor::SEVERITY_ERROR,
[ 'type' => $errorType, 'source' => 'admin_notice' ]
);
$class = 'notice notice-error';
printf(
'<div class="%1$s"><p>%2$s</p></div>',
Expand Down Expand Up @@ -772,6 +802,9 @@ public function init() {
// Add menu pages
add_action( 'admin_menu', array( self::class, 'addMenuPages' ) );

// Troubleshooting report download (admin_post_ = logged-in users only)
add_action( 'admin_post_' . TroubleshootingReport::AJAX_ACTION, array( TroubleshootingReport::class, 'handleDownload' ) );

// Filter body classes of admin pages
add_filter( 'admin_body_class', array( self::class, 'filterAdminBodyClass' ), 10, 1 );

Expand Down
4 changes: 2 additions & 2 deletions src/Repository/Timeframe.php
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ function ( $post ) {

return $timeframe->isBookable();
} catch ( Exception $e ) {
error_log( $e->getMessage() );
commonsbooking_write_log( $e->getMessage(), false );

return false;
}
Expand All @@ -721,7 +721,7 @@ function ( $post ) {
try {
return commonsbooking_isCurrentUserAllowedToBook( $post->ID );
} catch ( Exception $e ) {
error_log( $e->getMessage() );
commonsbooking_write_log( $e->getMessage(), false );

return false;
}
Expand Down
8 changes: 3 additions & 5 deletions src/Service/BookingRuleApplied.php
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,11 @@ function ( $rule ) {
);
if ( $json ) {
return $json;
} elseif ( WP_DEBUG ) {
error_log( 'Could not encode rules object.' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
} else {
commonsbooking_write_log( 'Could not encode rules object.', false );
}
} catch ( Exception $e ) {
if ( WP_DEBUG ) {
error_log( $e->getMessage() ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
}
commonsbooking_write_log( $e->getMessage(), false );
}
return ''; // Return an empty string if initialization or encoding fails
}
Expand Down
3 changes: 1 addition & 2 deletions src/Service/Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -501,8 +501,7 @@ private static function runShortcodeCalls( array $shortCodeCalls ): void {
try {
$class::$function( $attributes, $shortcodeBody );
} catch ( Exception $e ) {
// Writes error to log anyway
error_log( (string) $e ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
commonsbooking_write_log( (string) $e, false );
}
}
}
Expand Down
92 changes: 92 additions & 0 deletions src/Service/ErrorMonitor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php

namespace CommonsBooking\Service;

/**
* Persistent error ring-buffer stored in wp_options.
* Accessible to plugin admins without WP_DEBUG_LOG.
*/
class ErrorMonitor {

const OPTION_KEY = 'commonsbooking_error_log';
const MAX_ENTRIES = 100;

const SEVERITY_ERROR = 'error';
const SEVERITY_WARNING = 'warning';
const SEVERITY_INFO = 'info';

/**
* Records a message into the ring buffer.
*
* @param string $message Human-readable description.
* @param string $severity One of the SEVERITY_* constants.
* @param array $context Extra key/value context merged with auto-captured caller info.
* @param int $traceDepth Frames to skip in debug_backtrace (0 = direct caller of record()).
*/
public static function record(
string $message,
string $severity = self::SEVERITY_ERROR,
array $context = [],
int $traceDepth = 0
): void {
if ( empty( $message ) ) {
return;
}

// Capture caller — skip record() itself + $traceDepth additional frames
$bt = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, $traceDepth + 3 );
$frame = $bt[ $traceDepth + 1 ] ?? $bt[0];

$entry = [
'timestamp' => time(),
'severity' => $severity,
'message' => $message,
'context' => array_merge(
[
'file' => $frame['file'] ?? '',
'line' => $frame['line'] ?? 0,
],
$context
),
];

$log = get_option( self::OPTION_KEY, [] );
array_unshift( $log, $entry );
if ( count( $log ) > self::MAX_ENTRIES ) {
$log = array_slice( $log, 0, self::MAX_ENTRIES );
}
// autoload=false: this option can grow large; only load on-demand
update_option( self::OPTION_KEY, $log, false );
}

/**
* Returns the most recent entries, newest first.
*
* @param int $limit 0 returns up to MAX_ENTRIES.
* @return array<int, array{timestamp:int, severity:string, message:string, context:array}>
*/
public static function getEntries( int $limit = 50 ): array {
$log = get_option( self::OPTION_KEY, [] );
return $limit > 0 ? array_slice( $log, 0, $limit ) : $log;
}

/** Returns the total number of stored entries. */
public static function count(): int {
return count( get_option( self::OPTION_KEY, [] ) );
}

/** Returns the count of entries matching a specific severity level. */
public static function countBySeverity( string $severity ): int {
return count(
array_filter(
get_option( self::OPTION_KEY, [] ),
fn( $e ) => ( $e['severity'] ?? '' ) === $severity
)
);
}

/** Deletes all stored entries. */
public static function clear(): void {
delete_option( self::OPTION_KEY );
}
}
Loading
Loading