feat: add structured file-based logging with admin log viewer
- New WooList_Logger class writes to wp-content/uploads/woolist-logs/woolist.log
- INFO level: subscription events, test connection results (always recorded)
- ERROR level: API failures, config problems (always recorded + php error_log fallback)
- DEBUG level: full request URLs (password redacted), raw responses, step-by-step
flow (only when "Enable debug logging" is checked in settings)
- Auto-rotates at 1 MB; log directory protected by .htaccess
- API class: logs every request URL (redacted) and raw response body at DEBUG,
errors at ERROR; subscribe_email_to_list logs each step (lookup/create/add)
- Hooks class: logs hook fire, skip reasons, and sync intent at DEBUG/INFO/ERROR
- Shortcode class: logs AJAX submissions, coupon generation, and failures
- Admin: new Logging section with "Enable debug logging" checkbox;
log viewer textarea (last 300 lines, dark theme) + Clear Log button
both visible at bottom of WooCommerce → Settings → phpList tab
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,9 @@ class WooList_Admin {
|
||||
// Test connection admin-post handler.
|
||||
add_action( 'admin_post_woolist_test_connection', [ $this, 'handle_test_connection' ] );
|
||||
|
||||
// Clear log admin-post handler.
|
||||
add_action( 'admin_post_woolist_clear_log', [ $this, 'handle_clear_log' ] );
|
||||
|
||||
// Enqueue admin JS/CSS only on the WC settings page.
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_assets' ] );
|
||||
}
|
||||
@@ -44,6 +47,7 @@ class WooList_Admin {
|
||||
public function render_settings(): void {
|
||||
woocommerce_admin_fields( $this->get_settings() );
|
||||
$this->render_test_connection_button();
|
||||
$this->render_log_viewer();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,6 +83,48 @@ class WooList_Admin {
|
||||
. '</a></p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the log viewer panel below all settings.
|
||||
*/
|
||||
private function render_log_viewer(): void {
|
||||
$log_content = WooList_Logger::read_recent( 300 );
|
||||
$log_path = WooList_Logger::get_log_path();
|
||||
$clear_url = wp_nonce_url(
|
||||
admin_url( 'admin-post.php?action=woolist_clear_log' ),
|
||||
'woolist_clear_log'
|
||||
);
|
||||
|
||||
$notice = get_transient( 'woolist_clear_log_notice_' . get_current_user_id() );
|
||||
if ( $notice ) {
|
||||
delete_transient( 'woolist_clear_log_notice_' . get_current_user_id() );
|
||||
echo '<div class="notice notice-success inline"><p>' . esc_html( $notice ) . '</p></div>';
|
||||
}
|
||||
|
||||
echo '<hr style="margin:2em 0;">';
|
||||
echo '<h2>' . esc_html__( 'Activity Log', 'woolist-phplist' ) . '</h2>';
|
||||
echo '<p style="color:#646970;">';
|
||||
echo esc_html__( 'Log file: ', 'woolist-phplist' );
|
||||
echo '<code>' . esc_html( $log_path ) . '</code>';
|
||||
echo '</p>';
|
||||
|
||||
if ( $log_content === '' ) {
|
||||
echo '<p><em>' . esc_html__( 'Log is empty.', 'woolist-phplist' ) . '</em></p>';
|
||||
} else {
|
||||
echo '<div style="position:relative;">';
|
||||
echo '<textarea readonly rows="20" style="width:100%;font-family:monospace;font-size:12px;background:#1e1e1e;color:#d4d4d4;padding:12px;border:1px solid #ccc;border-radius:4px;resize:vertical;white-space:pre;">'
|
||||
. esc_textarea( $log_content )
|
||||
. '</textarea>';
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
echo '<p>';
|
||||
echo '<a href="' . esc_url( $clear_url ) . '" class="button button-secondary" '
|
||||
. 'onclick="return confirm(\'' . esc_js( __( 'Clear the entire log file?', 'woolist-phplist' ) ) . '\')">'
|
||||
. esc_html__( 'Clear Log', 'woolist-phplist' )
|
||||
. '</a>';
|
||||
echo '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the "Test Connection" form action.
|
||||
*/
|
||||
@@ -89,10 +135,13 @@ class WooList_Admin {
|
||||
wp_die( esc_html__( 'You do not have permission to perform this action.', 'woolist-phplist' ) );
|
||||
}
|
||||
|
||||
WooList_Logger::info( 'Test Connection triggered by user ID ' . get_current_user_id() );
|
||||
|
||||
$result = $this->api->lists_get();
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
WooList_Logger::error( 'Test Connection failed: ' . $result->get_error_message() );
|
||||
set_transient(
|
||||
'woolist_test_connection_notice_' . $user_id,
|
||||
[
|
||||
@@ -103,6 +152,7 @@ class WooList_Admin {
|
||||
);
|
||||
} else {
|
||||
$count = is_array( $result ) ? count( $result ) : '?';
|
||||
WooList_Logger::info( 'Test Connection succeeded, found ' . $count . ' list(s).' );
|
||||
set_transient(
|
||||
'woolist_test_connection_notice_' . $user_id,
|
||||
[
|
||||
@@ -121,6 +171,29 @@ class WooList_Admin {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the "Clear Log" admin-post action.
|
||||
*/
|
||||
public function handle_clear_log(): void {
|
||||
check_admin_referer( 'woolist_clear_log' );
|
||||
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_die( esc_html__( 'You do not have permission to perform this action.', 'woolist-phplist' ) );
|
||||
}
|
||||
|
||||
WooList_Logger::clear();
|
||||
WooList_Logger::info( 'Log cleared by user ID ' . get_current_user_id() );
|
||||
|
||||
set_transient(
|
||||
'woolist_clear_log_notice_' . get_current_user_id(),
|
||||
__( 'Log file cleared.', 'woolist-phplist' ),
|
||||
60
|
||||
);
|
||||
|
||||
wp_safe_redirect( admin_url( 'admin.php?page=wc-settings&tab=woolist' ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin-side assets on WC Settings → phpList tab.
|
||||
*/
|
||||
@@ -129,7 +202,6 @@ class WooList_Admin {
|
||||
if ( ! $screen || strpos( $screen->id, 'woocommerce' ) === false ) {
|
||||
return;
|
||||
}
|
||||
// No custom admin JS/CSS needed yet; WC handles the settings form.
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,6 +408,25 @@ class WooList_Admin {
|
||||
'type' => 'sectionend',
|
||||
'id' => 'woolist_section_newsletter',
|
||||
],
|
||||
|
||||
// ── Section 6: Logging ───────────────────────────────────────────
|
||||
[
|
||||
'title' => __( 'Logging', 'woolist-phplist' ),
|
||||
'type' => 'title',
|
||||
'desc' => __( 'Control what gets recorded in the activity log shown below.', 'woolist-phplist' ),
|
||||
'id' => 'woolist_section_logging',
|
||||
],
|
||||
[
|
||||
'title' => __( 'Enable debug logging', 'woolist-phplist' ),
|
||||
'desc' => __( 'Log full API request URLs (password redacted) and raw responses. Disable on production once everything works.', 'woolist-phplist' ),
|
||||
'id' => 'woolist_enable_debug_log',
|
||||
'type' => 'checkbox',
|
||||
'default' => 'no',
|
||||
],
|
||||
[
|
||||
'type' => 'sectionend',
|
||||
'id' => 'woolist_section_logging',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user