Skip to main content

Retries and Action Logs

Core Forms provides automatic retry logic for failed actions and structured logging for every action execution.

Automatic Retries

When an action fails during background processing, it is automatically retried with exponential backoff:

Attempt Delay Scheduled At
1st retry 2 minutes now + 120s
2nd retry 4 minutes now + 240s
3rd retry 8 minutes now + 480s

After 3 failed attempts (configurable via max_retries), the action is marked as failed.

Backoff Formula

delay = 2^attempts * 60 seconds

For attempt 1: 2^1 * 60 = 120s (2 minutes) For attempt 2: 2^2 * 60 = 240s (4 minutes) For attempt 3: 2^3 * 60 = 480s (8 minutes)

How Retries Work

// On failure, the queue updates the item:
if ( $attempts <= $max_retries ) {
    $delay = pow( 2, $attempts ) * 60;
    // Status set to 'retrying', scheduled_at set to future time
    $wpdb->update( $table, [
        'status'       => 'retrying',
        'attempts'     => $attempts,
        'last_error'   => $e->getMessage(),
        'scheduled_at' => gmdate( 'Y-m-d H:i:s', time() + $delay ),
    ], [ 'id' => $item->id ] );
} else {
    // All retries exhausted
    $wpdb->update( $table, [
        'status'    => 'failed',
        'attempts'  => $attempts,
        'last_error'=> $e->getMessage(),
    ], [ 'id' => $item->id ] );
}

Manual Retry

Failed actions can be manually retried via the REST API. This resets the attempt counter and re-queues the item:

# Retry a specific failed action
curl -X POST \
  https://example.com/wp-json/cf/v1/action-queue/42/retry \
  -H "X-WP-Nonce: {nonce}"
// Programmatic retry
use Core_Forms\Workflows\ActionQueue;
ActionQueue::retry( $queue_id ); // Returns true if reset for retry

The retry method only works on items with failed status. It resets attempts to 0 and sets scheduled_at to now.

Action Logger

The ActionLogger records every action execution with structured metadata. Logs are stored in the wp_cf_action_logs table.

Log Entry Structure

Column Type Description
id INT Primary key
queue_id INT Queue item ID (0 for synchronous actions)
form_id INT Form post ID
submission_id INT Submission ID
action_type VARCHAR(50) Action type (e.g., email, webhook)
status VARCHAR(20) success, failed, or skipped
message TEXT Human-readable message or error details
meta LONGTEXT JSON metadata (request/response data)
created_at TIMESTAMP When the log entry was created

Logging an Action

use Core_Forms\Workflows\ActionLogger;

// Log a successful action
ActionLogger::log(
    $queue_id,       // 0 for synchronous actions
    $form->ID,
    $submission->id,
    'webhook',       // action type
    'success',
    'Webhook delivered to https://api.example.com/hook',
    [ 'response_code' => 200, 'response_body' => '...' ]
);

// Log a failure
ActionLogger::log(
    $queue_id,
    $form->ID,
    $submission->id,
    'webhook',
    'failed',
    'Connection timeout after 30s'
);

Querying Logs

// Get logs for a specific submission
$logs = ActionLogger::get_for_submission( $submission_id );

// Get logs for a form (paginated)
$logs = ActionLogger::get_for_form( $form_id, $per_page = 20, $page = 1 );

// Get recent failures across all forms
$failures = ActionLogger::get_recent_failures( $limit = 20 );

// Get summary statistics
$stats = ActionLogger::get_stats( $form_id );
// Returns: [ 'total' => 150, 'success' => 140, 'failed' => 10, 'by_type' => [...] ]

Log Cleanup

Logs older than 90 days are automatically cleaned up:

$deleted = ActionLogger::cleanup( $days = 90 );

REST API Endpoints

List Action Logs

GET /wp-json/cf/v1/action-logs

Parameters: form_id, submission_id, status, per_page (default 20), page (default 1).

Log Statistics

GET /wp-json/cf/v1/action-logs/stats

Parameters: form_id (optional).

Returns counts by status and action type.

Recent Failures

GET /wp-json/cf/v1/action-logs/failures

Returns the 20 most recent failed log entries.

Retry a Failed Action

POST /wp-json/cf/v1/action-queue/{id}/retry

Resets the queue item for reprocessing. Returns { "retried": true } on success.

Test an Action

POST /wp-json/cf/v1/actions/test

Body:

{
    "action_type": "email",
    "form_id": 123,
    "settings": { "to": "test@example.com", "subject": "Test", "message": "Hello" },
    "test_data": { "name": "Test User", "email": "test@example.com" }
}

All endpoints require the edit_forms capability.

Related