Files
michaelschiemer/src/Framework/LiveComponents/docs/DEVTOOLS-DEBUGGING-GUIDE.md
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

30 KiB

LiveComponents DevTools & Debugging Guide

Umfassender Leitfaden für Debugging und Development Tools für LiveComponents.

Table of Contents

  1. Debug Mode
  2. Component State Inspection
  3. Request/Response Debugging
  4. Performance Profiling
  5. Common Debugging Scenarios
  6. Browser DevTools Integration
  7. Server-Side Logging
  8. Testing & Development Helpers
  9. Troubleshooting Workflows
  10. Production Debugging

Debug Mode

Enabling Debug Mode

use App\Framework\LiveComponents\Config\LiveComponentsConfig;

// In your configuration (e.g., config/livecomponents.php)
return new LiveComponentsConfig(
    debug: true,  // Enable debug mode
    enableDevTools: true,  // Enable DevTools helpers
    logLevel: 'debug'  // Verbose logging
);

Debug Mode Features

Automatic Features When Enabled:

  1. Verbose Logging: All component lifecycle events logged
  2. State Snapshots: Before/after state changes captured
  3. Performance Metrics: Render times, action execution times
  4. Error Context: Enhanced error messages with full state dump
  5. Request/Response Logging: All AJAX requests/responses logged

Environment-Based Configuration

// config/livecomponents.php
use App\Framework\Core\Environment;
use App\Framework\Config\EnvKey;

return new LiveComponentsConfig(
    debug: $container->get(Environment::class)->get(EnvKey::APP_ENV) === 'development',
    enableDevTools: $container->get(Environment::class)->get(EnvKey::APP_ENV) !== 'production',
    logLevel: match ($container->get(Environment::class)->get(EnvKey::APP_ENV)) {
        'production' => 'error',
        'staging' => 'warning',
        'development' => 'debug',
        default => 'info'
    }
);

Component State Inspection

Server-Side State Inspection

use App\Framework\LiveComponents\Debug\StateInspector;

final readonly class ShoppingCartComponent implements LiveComponentContract
{
    public function __construct(
        private StateInspector $stateInspector  // Injected when debug=true
    ) {}

    #[Action]
    public function addItem(CartItem $item): ShoppingCartState
    {
        // Capture state before action
        $this->stateInspector->captureSnapshot('before_add_item', $this->state);

        $newState = $this->state->addItem($item);

        // Capture state after action
        $this->stateInspector->captureSnapshot('after_add_item', $newState);

        // Analyze state diff
        $diff = $this->stateInspector->diff($this->state, $newState);
        $this->stateInspector->log('State changed', [
            'action' => 'addItem',
            'changes' => $diff,
            'item_count_before' => count($this->state->items),
            'item_count_after' => count($newState->items)
        ]);

        return $newState;
    }
}

Client-Side State Inspection

// Access component state from browser console
const component = window.LiveComponents.get('shopping-cart:main');

// Current state
console.log('Current State:', component.state);

// State history (when debug=true)
console.log('State History:', component._debugStateHistory);

// Last action result
console.log('Last Action:', component._lastAction);

// Component metadata
console.log('Component Info:', {
    id: component.id,
    name: component.name,
    version: component.version,
    renderCount: component._renderCount,
    actionCount: component._actionCount
});

State Snapshots

// Automatic snapshots during development
final readonly class StateSnapshot
{
    public function __construct(
        public string $label,
        public ComponentData $state,
        public float $timestamp,
        public ?string $triggeredBy = null,  // Action name or event
        public ?array $metadata = null
    ) {}
}

// Usage
$inspector->captureSnapshot('cart_loaded', $this->state, metadata: [
    'user_id' => $userId,
    'session_id' => $sessionId,
    'item_count' => count($this->state->items)
]);

// Retrieve snapshots
$snapshots = $inspector->getSnapshots($componentId);

// Compare snapshots
$diff = $inspector->compareSnapshots($snapshot1, $snapshot2);

Request/Response Debugging

Server-Side Request Logging

use App\Framework\LiveComponents\Debug\RequestLogger;

final readonly class LiveComponentsRequestLogger
{
    public function logRequest(
        string $componentId,
        string $action,
        array $parameters,
        ComponentData $stateBefore,
        ComponentData $stateAfter,
        float $executionTime
    ): void {
        $this->logger->debug('LiveComponent Action', [
            'component_id' => $componentId,
            'action' => $action,
            'parameters' => $parameters,
            'execution_time_ms' => round($executionTime * 1000, 2),
            'state_diff' => $this->calculateDiff($stateBefore, $stateAfter),
            'timestamp' => microtime(true)
        ]);
    }
}

Client-Side Network Debugging

// Enable detailed network logging
window.LiveComponents.setDebug(true);

// Network request interceptor (when debug=true)
window.LiveComponents.onRequest((request) => {
    console.group(`📤 LiveComponent Request: ${request.action}`);
    console.log('Component:', request.componentId);
    console.log('Action:', request.action);
    console.log('Parameters:', request.parameters);
    console.log('State Hash:', request.stateHash);
    console.groupEnd();
});

window.LiveComponents.onResponse((response) => {
    console.group(`📥 LiveComponent Response: ${response.action}`);
    console.log('Duration:', `${response.duration}ms`);
    console.log('State Updated:', response.stateChanged);
    console.log('Fragments:', response.fragments?.length || 0);
    console.log('Errors:', response.errors || 'None');
    console.groupEnd();
});

window.LiveComponents.onError((error) => {
    console.group(`❌ LiveComponent Error: ${error.action}`);
    console.error('Message:', error.message);
    console.error('Component:', error.componentId);
    console.error('Stack:', error.stack);
    console.groupEnd();
});

Browser DevTools Network Panel

Request Inspection:

POST /livecomponent/action
Headers:
  X-LiveComponent: true
  X-Component-Id: shopping-cart:main
  X-Action: addItem
  X-State-Hash: a1b2c3d4e5f6

Request Payload:
{
  "item": {
    "id": "prod-123",
    "name": "Product Name",
    "price": 2999,
    "quantity": 1
  }
}

Response:
{
  "success": true,
  "stateHash": "f6e5d4c3b2a1",
  "fragments": [
    {
      "selector": "#cart-items",
      "html": "<div class='cart-item'>...</div>"
    },
    {
      "selector": "#cart-total",
      "html": "<span>€29.99</span>"
    }
  ],
  "meta": {
    "executionTime": 23.45,
    "cacheHit": false,
    "fragmentCount": 2
  }
}

Performance Profiling

Server-Side Performance Metrics

use App\Framework\LiveComponents\Performance\ComponentProfiler;

final readonly class PerformanceProfiler
{
    public function profileAction(
        string $componentId,
        string $action,
        callable $actionHandler
    ): ProfilingResult {
        $startTime = microtime(true);
        $startMemory = memory_get_usage(true);

        // Execute action
        $result = $actionHandler();

        $endTime = microtime(true);
        $endMemory = memory_get_usage(true);

        return new ProfilingResult(
            componentId: $componentId,
            action: $action,
            executionTime: Duration::fromSeconds($endTime - $startTime),
            memoryUsed: $endMemory - $startMemory,
            peakMemory: memory_get_peak_usage(true),
            result: $result
        );
    }
}

// Usage in component
#[Action]
public function processPayment(PaymentRequest $request): ComponentData
{
    return $this->profiler->profileAction(
        $this->id->toString(),
        'processPayment',
        fn() => $this->executePayment($request)
    )->result;
}

Component Lifecycle Profiling

final readonly class LifecycleProfiler
{
    public function profileRender(LiveComponentContract $component): RenderProfile
    {
        $metrics = [
            'template_load' => $this->measureTemplateLoad($component),
            'state_serialization' => $this->measureStateSerialization($component),
            'html_generation' => $this->measureHtmlGeneration($component),
            'dom_manipulation' => $this->measureDomManipulation($component)
        ];

        return new RenderProfile(
            componentId: $component->id,
            totalTime: array_sum($metrics),
            breakdown: $metrics,
            cacheHit: $this->wasCached($component)
        );
    }
}

Client-Side Performance Metrics

// Performance API integration
const performanceObserver = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
        if (entry.name.startsWith('livecomponent:')) {
            console.log('Performance Entry:', {
                name: entry.name,
                duration: `${entry.duration.toFixed(2)}ms`,
                startTime: entry.startTime,
                type: entry.entryType
            });
        }
    }
});

performanceObserver.observe({ entryTypes: ['measure'] });

// Measure action execution
performance.mark('livecomponent:action:start');
await component.call('addItem', { item });
performance.mark('livecomponent:action:end');

performance.measure(
    'livecomponent:action:addItem',
    'livecomponent:action:start',
    'livecomponent:action:end'
);

Render Performance Tracking

// Track rendering performance
class RenderPerformanceTracker {
    trackRender(componentId, metrics) {
        const entry = {
            componentId,
            timestamp: Date.now(),
            domPatchTime: metrics.domPatchTime,
            fragmentCount: metrics.fragmentCount,
            bytesReceived: metrics.bytesReceived,
            cacheHit: metrics.cacheHit
        };

        // Store in IndexedDB for analysis
        this.storeMetric(entry);

        // Warn on slow renders
        if (metrics.domPatchTime > 100) {
            console.warn(`Slow render detected: ${componentId}`, metrics);
        }
    }

    getAverageRenderTime(componentId) {
        const entries = this.getMetrics(componentId);
        return entries.reduce((sum, e) => sum + e.domPatchTime, 0) / entries.length;
    }
}

Common Debugging Scenarios

Scenario 1: Action Not Triggering

Symptoms: Button click doesn't trigger action

Debug Steps:

// 1. Check if component is registered
const component = window.LiveComponents.get('shopping-cart:main');
console.log('Component exists:', !!component);

// 2. Check action attribute
const button = document.querySelector('[data-action="addItem"]');
console.log('Action attribute:', button?.dataset.action);
console.log('Component ID attribute:', button?.dataset.componentId);

// 3. Check event listeners
console.log('Event listeners:', getEventListeners(button));

// 4. Enable verbose logging
window.LiveComponents.setDebug(true);

// 5. Try manual action call
component.call('addItem', { item: testItem })
    .then(result => console.log('Manual call success:', result))
    .catch(error => console.error('Manual call failed:', error));

Common Fixes:

  • Missing data-component-id attribute
  • Incorrect action name (case-sensitive)
  • Component not initialized (check initialization order)
  • JavaScript errors preventing event binding

Scenario 2: State Not Updating

Symptoms: Server returns success but UI doesn't update

Debug Steps:

// Server-side: Verify state change
#[Action]
public function addItem(CartItem $item): ShoppingCartState
{
    $oldState = $this->state;
    $newState = $this->state->addItem($item);

    // Debug logging
    error_log("Old item count: " . count($oldState->items));
    error_log("New item count: " . count($newState->items));
    error_log("State changed: " . ($oldState !== $newState ? 'yes' : 'no'));

    return $newState;
}
// Client-side: Track state updates
window.LiveComponents.onStateChange((event) => {
    console.log('State changed:', {
        componentId: event.componentId,
        oldHash: event.oldHash,
        newHash: event.newHash,
        changed: event.oldHash !== event.newHash
    });
});

Common Fixes:

  • State object not immutable (modifying instead of returning new instance)
  • Missing fragments in response
  • DomPatcher not applying changes (check selectors)
  • Cache preventing updates (clear cache)

Scenario 3: Memory Leaks

Symptoms: Browser memory grows over time

Debug Steps:

// Heap snapshot comparison
// 1. Take baseline snapshot
console.log('Taking baseline snapshot...');
// Use Chrome DevTools > Memory > Take snapshot

// 2. Perform actions
for (let i = 0; i < 100; i++) {
    await component.call('addItem', { item: generateTestItem(i) });
}

// 3. Take second snapshot
console.log('Taking second snapshot...');
// Compare snapshots in DevTools

// Check component cleanup
window.addEventListener('beforeunload', () => {
    console.log('Active components:', window.LiveComponents.getAll());
    console.log('Event listeners:', window.LiveComponents._eventListeners.size);
});

Common Fixes:

  • Event listeners not cleaned up on component destroy
  • DOM references held in closures
  • Polling not stopped on unmount
  • Large state objects not garbage collected

Scenario 4: CSRF Token Mismatch

Symptoms: 403 Forbidden on action calls

Debug Steps:

// Check CSRF token presence
const form = document.querySelector('form');
const csrfToken = form.querySelector('[name="_csrf_token"]')?.value;
console.log('CSRF Token in form:', csrfToken);

// Check request headers
window.LiveComponents.onRequest((request) => {
    console.log('CSRF Token in request:', request.headers['X-CSRF-Token']);
});

// Check session token
fetch('/api/csrf-token')
    .then(r => r.json())
    .then(data => console.log('Current session CSRF:', data.token));

Common Fixes:

  • Token not included in request (check middleware)
  • Token expired (implement rotation)
  • Session cleared (re-authenticate)
  • Token not matching session (clear cookies and refresh)

Scenario 5: Rate Limiting Issues

Symptoms: Actions suddenly fail with 429 Too Many Requests

Debug Steps:

// Track rate limit headers
window.LiveComponents.onResponse((response) => {
    if (response.status === 429) {
        console.error('Rate Limited:', {
            retryAfter: response.headers['Retry-After'],
            limit: response.headers['X-RateLimit-Limit'],
            remaining: response.headers['X-RateLimit-Remaining'],
            reset: response.headers['X-RateLimit-Reset']
        });
    }
});

// Monitor action frequency
let actionCount = 0;
let startTime = Date.now();

window.LiveComponents.onRequest(() => {
    actionCount++;
    const elapsed = (Date.now() - startTime) / 1000;
    const rate = actionCount / elapsed;

    console.log(`Action rate: ${rate.toFixed(2)} req/sec`);

    if (rate > 5) {
        console.warn('High action rate detected - may trigger rate limiting');
    }
});

Common Fixes:

  • Implement debouncing for user actions
  • Increase rate limits for legitimate use cases
  • Add exponential backoff on 429 responses
  • Batch multiple actions into single request

Browser DevTools Integration

Console Helpers

// Global debug helpers (available in console)

// Get component by ID
lc('shopping-cart:main')  // Shorthand for window.LiveComponents.get()

// List all components
lc.all()

// Component state
lc.state('shopping-cart:main')

// Component history
lc.history('shopping-cart:main')

// Trigger action manually
lc.call('shopping-cart:main', 'addItem', { item: testItem })

// Enable/disable debug mode
lc.debug(true)  // Enable
lc.debug(false) // Disable

// Performance stats
lc.stats('shopping-cart:main')

// Clear component cache
lc.clearCache('shopping-cart:main')

React DevTools Style Inspector

// Component tree visualization
window.LiveComponents.inspect()
// Logs component hierarchy:
/*
shopping-cart:main
├── cart-item:item-1
├── cart-item:item-2
└── cart-total:main
*/

// Component details
window.LiveComponents.inspectComponent('shopping-cart:main')
// Logs:
/*
Component: shopping-cart:main
State: { items: [...], total: 2999 }
Actions: [addItem, removeItem, updateQuantity]
Events: [itemAdded, itemRemoved]
Polling: { enabled: true, interval: 5000ms }
Cache: { enabled: true, ttl: 300s, hits: 23, misses: 5 }
Performance: { avgRenderTime: 15ms, actionCount: 47 }
*/

Server-Side Logging

Structured Logging

use App\Framework\LiveComponents\Logging\ComponentLogger;
use Psr\Log\LogLevel;

final readonly class ComponentLogger
{
    public function logAction(
        string $componentId,
        string $action,
        array $context = []
    ): void {
        $this->logger->log(LogLevel::INFO, 'LiveComponent action executed', [
            'component_id' => $componentId,
            'action' => $action,
            'user_id' => $context['user_id'] ?? null,
            'session_id' => $context['session_id'] ?? null,
            'execution_time_ms' => $context['execution_time'] ?? null,
            'timestamp' => time()
        ]);
    }

    public function logError(
        string $componentId,
        string $action,
        \Throwable $error,
        array $context = []
    ): void {
        $this->logger->log(LogLevel::ERROR, 'LiveComponent action failed', [
            'component_id' => $componentId,
            'action' => $action,
            'error_message' => $error->getMessage(),
            'error_class' => get_class($error),
            'stack_trace' => $error->getTraceAsString(),
            'state_snapshot' => $context['state'] ?? null,
            'parameters' => $context['parameters'] ?? null
        ]);
    }
}

Action Audit Log

final readonly class ActionAuditLogger
{
    public function logActionExecution(
        ActionExecution $execution
    ): void {
        $this->auditLog->write([
            'event' => 'livecomponent.action',
            'component_id' => $execution->componentId,
            'action' => $execution->action,
            'user_id' => $execution->userId,
            'ip_address' => $execution->ipAddress,
            'user_agent' => $execution->userAgent,
            'parameters' => $this->sanitizeParameters($execution->parameters),
            'success' => $execution->success,
            'error' => $execution->error?->getMessage(),
            'execution_time_ms' => $execution->executionTime->toMilliseconds(),
            'timestamp' => $execution->timestamp->format('Y-m-d H:i:s')
        ]);
    }

    private function sanitizeParameters(array $parameters): array
    {
        // Remove sensitive data from logs
        $sanitized = $parameters;

        foreach (['password', 'token', 'api_key', 'secret'] as $sensitive) {
            if (isset($sanitized[$sensitive])) {
                $sanitized[$sensitive] = '[REDACTED]';
            }
        }

        return $sanitized;
    }
}

Log Aggregation Queries

# Find slow actions (Elasticsearch/Kibana query)
GET /livecomponents-logs/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "event": "livecomponent.action" } },
        { "range": { "execution_time_ms": { "gte": 100 } } }
      ]
    }
  },
  "aggs": {
    "slow_actions": {
      "terms": {
        "field": "action.keyword",
        "size": 10
      },
      "aggs": {
        "avg_time": { "avg": { "field": "execution_time_ms" } }
      }
    }
  }
}

# Find error patterns
GET /livecomponents-logs/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "success": false } }
      ]
    }
  },
  "aggs": {
    "error_types": {
      "terms": { "field": "error_class.keyword" }
    },
    "affected_components": {
      "terms": { "field": "component_id.keyword" }
    }
  }
}

Testing & Development Helpers

Pest Testing Helpers

use function Pest\LiveComponents\mountComponent;
use function Pest\LiveComponents\callAction;

// Mount component with debug enabled
it('debugs component state changes', function () {
    $component = mountComponent('shopping-cart:main', [
        'items' => [],
        'total' => 0
    ], debug: true);  // Enable debug mode

    // Call action with debug output
    $result = callAction($component, 'addItem', [
        'item' => ['id' => '1', 'name' => 'Test', 'price' => 1000]
    ], debug: true);

    // Debug output automatically logged:
    /*
    [DEBUG] Action: addItem
    [DEBUG] Before State: {"items":[],"total":0}
    [DEBUG] Parameters: {"item":{"id":"1","name":"Test","price":1000}}
    [DEBUG] After State: {"items":[...],"total":1000}
    [DEBUG] Execution Time: 2.34ms
    */

    expect($result['state']['total'])->toBe(1000);
});

Mock State Inspector

// Create mock state inspector for testing
final readonly class MockStateInspector implements StateInspector
{
    private array $snapshots = [];
    private array $logs = [];

    public function captureSnapshot(string $label, ComponentData $state, ?array $metadata = null): void
    {
        $this->snapshots[] = new StateSnapshot($label, $state, microtime(true), metadata: $metadata);
    }

    public function getSnapshots(string $componentId): array
    {
        return array_filter(
            $this->snapshots,
            fn($s) => str_starts_with($s->label, $componentId)
        );
    }

    public function log(string $message, array $context = []): void
    {
        $this->logs[] = ['message' => $message, 'context' => $context, 'time' => microtime(true)];
    }

    public function getLogs(): array
    {
        return $this->logs;
    }
}

// Use in tests
it('captures state snapshots during action', function () {
    $inspector = new MockStateInspector();
    $component = new ShoppingCartComponent(
        id: ComponentId::fromString('cart:test'),
        state: new ShoppingCartState([], 0),
        stateInspector: $inspector
    );

    $component->addItem(new CartItem('1', 'Test', 1000));

    $snapshots = $inspector->getSnapshots('cart:test');
    expect($snapshots)->toHaveCount(2)  // before and after
        ->and($inspector->getLogs())->toContain(fn($log) => $log['message'] === 'State changed');
});

Troubleshooting Workflows

Workflow 1: Component Not Rendering

1. Check component registration
   → Verify #[LiveComponent] attribute present
   → Check component name matches template usage

2. Check initialization
   → Console: window.LiveComponents.get('component:id')
   → Should return component instance

3. Verify template
   → Check render() method returns correct template path
   → Verify template file exists
   → Check template syntax

4. Check state
   → Verify state class is readonly
   → Check constructor parameters
   → Ensure state serializable

5. Server logs
   → Check for component registration errors
   → Look for template loading errors
   → Verify DI container bindings

Workflow 2: Action Fails Silently

1. Enable debug mode
   → Set debug: true in config
   → Check browser console for errors

2. Network inspection
   → DevTools > Network > Filter: livecomponent
   → Check request payload
   → Verify response status (200/403/429/500)

3. Server-side debugging
   → Add error_log() in action method
   → Check action parameter types match
   → Verify return type is ComponentData

4. State validation
   → Ensure new state is returned (not modified)
   → Check state is serializable
   → Verify no circular references

5. Security checks
   → CSRF token present and valid
   → Rate limit not exceeded
   → Authentication/authorization passed

Workflow 3: Performance Degradation

1. Measure baseline
   → Use Performance API to measure render times
   → Track action execution times
   → Monitor network request sizes

2. Identify bottleneck
   → Profile component lifecycle
   → Check database query count (N+1 problems)
   → Verify cache effectiveness

3. Optimize rendering
   → Enable fragment rendering
   → Implement component caching with varyBy
   → Use SWR for non-critical updates

4. Reduce network overhead
   → Enable request batching
   → Compress responses (gzip)
   → Implement polling backoff

5. Server-side optimization
   → Add database indexes
   → Optimize queries (eager loading)
   → Implement query result caching
   → Use Redis for session storage

Production Debugging

Safe Production Debugging

// Enable debug mode for specific users only
final readonly class DebugModeManager
{
    public function isDebugEnabledForUser(?string $userId): bool
    {
        if (!$userId) {
            return false;
        }

        // Allow debug mode for admin users
        if ($this->userService->isAdmin($userId)) {
            return true;
        }

        // Allow debug mode via feature flag
        if ($this->featureFlags->isEnabled('livecomponents-debug', $userId)) {
            return true;
        }

        return false;
    }
}

// Conditional debug logging
$config = new LiveComponentsConfig(
    debug: $this->debugManager->isDebugEnabledForUser($currentUserId),
    enableDevTools: false,  // Never enable in production
    logLevel: 'error'  // Only log errors in production
);

Production Error Tracking

use App\Framework\ErrorReporting\ErrorReporter;

final readonly class ProductionErrorHandler
{
    public function __construct(
        private ErrorReporter $errorReporter  // e.g., Sentry, Rollbar
    ) {}

    public function handleActionError(
        string $componentId,
        string $action,
        \Throwable $error,
        array $context
    ): void {
        $this->errorReporter->captureException($error, [
            'tags' => [
                'component' => 'livecomponents',
                'component_id' => $componentId,
                'action' => $action
            ],
            'extra' => [
                'user_id' => $context['user_id'] ?? null,
                'session_id' => $context['session_id'] ?? null,
                'state_hash' => $context['state_hash'] ?? null,
                'parameters' => $this->sanitize($context['parameters'] ?? [])
            ],
            'fingerprint' => [
                $componentId,
                $action,
                get_class($error)
            ]
        ]);
    }
}

Production Monitoring

// Metrics collection for production monitoring
final readonly class ProductionMetricsCollector
{
    public function recordActionExecution(
        string $componentId,
        string $action,
        float $executionTime,
        bool $success
    ): void {
        // StatsD/Prometheus metrics
        $this->metrics->increment('livecomponents.action.count', [
            'component' => $componentId,
            'action' => $action,
            'success' => $success ? 'true' : 'false'
        ]);

        $this->metrics->timing('livecomponents.action.duration', $executionTime, [
            'component' => $componentId,
            'action' => $action
        ]);
    }

    public function recordCacheHit(string $componentId, bool $hit): void
    {
        $this->metrics->increment('livecomponents.cache.' . ($hit ? 'hit' : 'miss'), [
            'component' => $componentId
        ]);
    }
}

Feature Flags for Debugging

// Use feature flags to enable debugging features safely
final readonly class FeatureFlagDebugger
{
    public function shouldEnableVerboseLogging(string $componentId): bool
    {
        return $this->featureFlags->isEnabled("debug:verbose:{$componentId}");
    }

    public function shouldCaptureSnapshots(string $componentId): bool
    {
        return $this->featureFlags->isEnabled("debug:snapshots:{$componentId}");
    }

    public function shouldProfilePerformance(string $componentId): bool
    {
        return $this->featureFlags->isEnabled("debug:profiling:{$componentId}");
    }
}

// Usage in component
#[Action]
public function processPayment(PaymentRequest $request): ComponentData
{
    if ($this->debugger->shouldCaptureSnapshots($this->id->toString())) {
        $this->stateInspector->captureSnapshot('before_payment', $this->state);
    }

    $result = $this->paymentService->process($request);

    if ($this->debugger->shouldCaptureSnapshots($this->id->toString())) {
        $this->stateInspector->captureSnapshot('after_payment', $this->state);
    }

    return $result;
}

Summary

Quick Reference

Enable Debug Mode:

new LiveComponentsConfig(debug: true, enableDevTools: true, logLevel: 'debug')

Client-Side Debugging:

window.LiveComponents.setDebug(true);
lc('component:id').state;  // Inspect state
lc.call('component:id', 'action', params);  // Manual action

Server-Side Debugging:

$this->stateInspector->captureSnapshot('label', $state);
$this->logger->debug('Action executed', $context);

Performance Profiling:

performance.mark('start');
await action();
performance.mark('end');
performance.measure('action', 'start', 'end');

Production Monitoring:

$this->errorReporter->captureException($error, $context);
$this->metrics->timing('action.duration', $time);

Best Practices

  1. Development: Enable debug mode and DevTools
  2. Staging: Enable selective debug for specific users
  3. Production: Disable debug, use error reporting and metrics
  4. Logging: Use structured logging with proper context
  5. Performance: Profile regularly, set performance budgets
  6. Security: Never log sensitive data (passwords, tokens)
  7. Monitoring: Set up alerts for error rates and slow actions
  8. Testing: Use debug helpers in Pest tests

Resources

  • Browser DevTools: Chrome/Firefox/Edge Developer Tools
  • Error Tracking: Sentry, Rollbar, Bugsnag
  • Metrics: StatsD, Prometheus, Datadog
  • Logging: ELK Stack, Graylog, Splunk
  • Profiling: Blackfire, Tideways, XHProf
  • Testing: Pest, PHPUnit, Playwright

This guide covers debugging workflows, tools, and best practices for developing and maintaining LiveComponents in development, staging, and production environments.