swoole-eloquent / profiler
Performance profiler for swoole-eloquent library with coroutine-aware metrics tracking
Requires
- php: ^8.1
- ext-swoole: >=5.0
- illuminate/database: ^10.0|^11.0
- illuminate/support: ^10.0|^11.0
- swoole-eloquent/swoole-eloquent: *
Requires (Dev)
- mockery/mockery: ^1.6
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0
This package is auto-updated.
Last update: 2026-03-05 06:25:07 UTC
README
A high-performance, coroutine-aware profiler for the swoole-eloquent library. Track query execution time, connection pool metrics, transaction lifecycles, and more with minimal overhead.
Features
- Coroutine-Aware: Properly isolates profiling data between concurrent coroutines
- Query Profiling: Track SQL queries with execution time, bindings, and success/failure status
- Pool Metrics: Monitor connection pool utilization, wait times, and exhaustion
- Transaction Tracking: Profile transaction duration and query count
- Request Aggregation: Aggregate metrics per HTTP request or coroutine
- Multiple Output Formats: CLI, JSON, and HTML reports
- Laravel Integration: First-class support with service provider and middleware
- Zero Configuration: Works out of the box with sensible defaults
- Minimal Overhead: Non-blocking design with efficient storage
Requirements
- PHP 8.1 or higher
- Swoole extension 5.0 or higher
- Laravel 10.x or 11.x (for Laravel integration)
Installation
composer require swoole-eloquent/profiler
Quick Start
Basic Usage
use SwooleEloquent\Connection\SwoolePostgresConnection; use SwooleEloquent\Connection\SwoolePostgresPool; use SwooleProfiler\Decorators\ProfiledConnection; use SwooleProfiler\Decorators\ProfiledPool; use SwooleProfiler\Profiler; use SwooleProfiler\Reporters\CliReporter; // Create your pool and connection $pool = new SwoolePostgresPool($config, 10); $connection = new SwoolePostgresConnection($pool, 'pgsql'); // Wrap with profiler decorators $profiledPool = new ProfiledPool($pool); $profiledConnection = new ProfiledConnection($connection); // Use as normal go(function () use ($profiledConnection) { $results = $profiledConnection->select('SELECT * FROM users'); // Get metrics $profiler = Profiler::getInstance(); $reporter = new CliReporter($profiler); echo $reporter->report(); });
Laravel Integration
The profiler automatically registers via package discovery.
Configuration
Publish the configuration file (optional):
php artisan vendor:publish --tag=profiler-config
Configure via .env:
PROFILER_ENABLED=true PROFILER_SLOW_QUERY_THRESHOLD=100.0 PROFILER_AUTO_PROFILE_REQUESTS=true PROFILER_ADD_HEADERS=true
Usage in Controllers
use SwooleProfiler\Laravel\Facades\Profiler; class UserController extends Controller { public function index() { // Queries are automatically profiled $users = User::async()->where('active', true)->get(); // Get current request metrics $profile = Profiler::getCurrentRequest(); return response()->json([ 'users' => $users, 'queries' => $profile?->getQueryCount(), 'duration' => $profile?->getTotalQueryTime(), ]); } public function profilerReport() { return response()->json(Profiler::getMetrics()); } }
Architecture
The profiler uses a decorator pattern to wrap connections and pools without modifying the original swoole-eloquent code:
┌─────────────────────────────────────────┐
│ Your Application │
└─────────────────┬───────────────────────┘
│
┌───────▼────────┐
│ ProfiledConnection │
└───────┬────────┘
│ (decorates)
┌───────▼────────┐
│ SwooleConnection │
└───────┬────────┘
│
┌───────▼────────┐
│ ProfiledPool │
└───────┬────────┘
│ (decorates)
┌───────▼────────┐
│ SwoolePool │
└─────────────────┘
Components
Data Classes
- QueryProfile: Stores information about a single query execution
- PoolProfile: Captures connection pool state at a point in time
- TransactionProfile: Tracks transaction lifecycle
- RequestProfile: Aggregates metrics for an entire request/coroutine
Storage
- ProfilerStorage: Coroutine-aware storage using
CoroutineContext - MetricsAggregator: Calculates statistics and aggregates data
Decorators
- ProfiledConnection: Wraps database connections to intercept queries
- ProfiledPool: Wraps connection pools to track metrics
Reporters
- CliReporter: Console output with formatted tables
- JsonReporter: JSON format for APIs and monitoring tools
- HtmlReporter: Web-based dashboard
Usage Examples
Profiling a Single Request
$profiler = Profiler::getInstance(); // Start profiling $profiler->startRequest('/api/users', 'GET'); // Execute queries $users = $connection->select('SELECT * FROM users WHERE active = ?', [true]); // End profiling $profile = $profiler->endRequest(); echo "Queries: " . $profile->getQueryCount() . "\n"; echo "Duration: " . $profile->getTotalQueryTime() . " ms\n";
Getting Slow Queries
$profiler = Profiler::getInstance(); // Set threshold (default: 100ms) $profiler->setSlowQueryThreshold(50.0); // Get slow queries $slowQueries = $profiler->getSlowQueries(); foreach ($slowQueries as $query) { echo sprintf( "[%.2f ms] %s\n", $query->duration, $query->sql ); }
Transaction Profiling
$profiler = Profiler::getInstance(); $connection->beginTransaction(); // Transaction is automatically tracked $connection->insert('INSERT INTO users (name) VALUES (?)', ['John']); $connection->update('UPDATE users SET active = ? WHERE id = ?', [true, 1]); $connection->commit(); // Transaction end is recorded // Get transaction metrics $metrics = $profiler->getMetrics(); print_r($metrics['transactions']);
Multiple Output Formats
use SwooleProfiler\Reporters\CliReporter; use SwooleProfiler\Reporters\JsonReporter; use SwooleProfiler\Reporters\HtmlReporter; $profiler = Profiler::getInstance(); // CLI output $cliReporter = new CliReporter($profiler); echo $cliReporter->report(); // JSON output $jsonReporter = new JsonReporter($profiler); file_put_contents('profiler.json', $jsonReporter->report()); // HTML output $htmlReporter = new HtmlReporter($profiler); file_put_contents('profiler.html', $htmlReporter->report());
HTTP Server Integration
use Swoole\Http\Server; use SwooleProfiler\Profiler; $server = new Server('0.0.0.0', 9501); $server->on('Request', function ($request, $response) { $profiler = Profiler::getInstance(); // Start profiling $profiler->startRequest($request->server['request_uri']); // Handle request $users = $connection->select('SELECT * FROM users'); // End profiling $profile = $profiler->endRequest(); // Add headers $response->header('X-Query-Count', (string)$profile->getQueryCount()); $response->header('X-Query-Time', sprintf('%.2f', $profile->getTotalQueryTime())); $response->end(json_encode(['users' => $users])); }); $server->start();
Metrics Reference
Summary Metrics
total_queries: Total number of queries executedsuccessful_queries: Number of successful queriesfailed_queries: Number of failed queriestotal_requests: Number of profiled requeststotal_query_time: Total time spent executing queries (ms)total_pool_wait_time: Total time spent waiting for connections (ms)
Query Metrics
avg_duration: Average query execution timemin_duration: Fastest query timemax_duration: Slowest query timequeries_by_type: Breakdown by query type (SELECT, INSERT, UPDATE, DELETE)
Pool Metrics
avg_utilization: Average pool utilization percentagemax_utilization: Peak pool utilizationexhaustion_count: Number of times pool was exhausted
Transaction Metrics
total: Total transactionscommitted: Successfully committed transactionsrolled_back: Rolled back transactionsavg_duration: Average transaction durationavg_queries_per_transaction: Average queries per transaction
Configuration
Via Config File (Laravel)
// config/profiler.php return [ 'enabled' => true, 'slow_query_threshold' => 100.0, 'auto_profile_requests' => true, 'auto_register_middleware' => false, 'listen_query_events' => false, 'add_headers' => true, 'add_to_response' => false, 'storage' => [ 'max_queries' => 1000, 'max_requests' => 100, ], ];
Via Code
$profiler = Profiler::getInstance(); // Enable/disable $profiler->enable(); $profiler->disable(); // Set slow query threshold $profiler->setSlowQueryThreshold(50.0); // Clear data $profiler->clear();
Performance Considerations
The profiler is designed for minimal overhead:
- Uses native
microtime(true)for timing - Leverages
CoroutineContextfor efficient per-coroutine storage - Non-blocking operations throughout
- Minimal memory footprint (~2KB per request)
Typical overhead: < 1% in production environments
Best Practices
- Enable only in development/staging: Disable in production or use sampling
- Set appropriate thresholds: Tune slow query threshold based on your needs
- Clear data periodically: Use
$profiler->clear()to prevent memory growth - Use appropriate reporters: CLI for development, JSON for monitoring tools
- Monitor pool metrics: Watch for exhaustion warnings
Troubleshooting
High Pool Wait Times
Increase connection pool size or optimize query execution time.
// Increase pool size $pool = new SwoolePostgresPool($config, 20); // Increased from 10
Memory Growth
Clear profiling data periodically:
// Clear after each batch of requests if ($profiler->getStorage()->getTotalRequestCount() > 100) { $profiler->clear(); }
Missing Queries
Ensure you're using the profiled decorators:
// ✗ Wrong $connection = new SwoolePostgresConnection($pool, 'pgsql'); // ✓ Correct $baseConnection = new SwoolePostgresConnection($pool, 'pgsql'); $connection = new ProfiledConnection($baseConnection);
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
License
MIT License