- 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.
30 KiB
LiveComponents DevTools & Debugging Guide
Umfassender Leitfaden für Debugging und Development Tools für LiveComponents.
Table of Contents
- Debug Mode
- Component State Inspection
- Request/Response Debugging
- Performance Profiling
- Common Debugging Scenarios
- Browser DevTools Integration
- Server-Side Logging
- Testing & Development Helpers
- Troubleshooting Workflows
- 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:
- Verbose Logging: All component lifecycle events logged
- State Snapshots: Before/after state changes captured
- Performance Metrics: Render times, action execution times
- Error Context: Enhanced error messages with full state dump
- 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-idattribute - 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
- Development: Enable debug mode and DevTools
- Staging: Enable selective debug for specific users
- Production: Disable debug, use error reporting and metrics
- Logging: Use structured logging with proper context
- Performance: Profile regularly, set performance budgets
- Security: Never log sensitive data (passwords, tokens)
- Monitoring: Set up alerts for error rates and slow actions
- 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.