aeatech/snapshot-profiler-xhprof
Installation
composer require aeatech/snapshot-profiler-xhprof
Enable XHProf
xhprof PHP extension is installed and enabled in php.ini:
extension=xhprof.so
php -m | grep xhprof
Basic Configuration
Add to config/app.php:
'snapshot-profiler' => [
'driver' => \Aeatech\SnapshotProfiler\XhProf\XhProfDriver::class,
'storage' => storage_path('app/profiler'),
],
First Profiling Run Use the facade to trigger profiling:
use Aeatech\SnapshotProfiler\Facades\Profiler;
Profiler::start('user-action');
// Your code to profile...
Profiler::stop();
View Results
Profiles are saved to storage/app/profiler as JSON files. Use tools like XHProf UI to analyze.
Middleware for Automatic Profiling Create middleware to profile requests:
namespace App\Http\Middleware;
use Aeatech\SnapshotProfiler\Facades\Profiler;
use Closure;
class ProfileRequests
{
public function handle($request, Closure $next)
{
Profiler::start('request-' . $request->path());
$response = $next($request);
Profiler::stop();
return $response;
}
}
Register in app/Http/Kernel.php:
protected $middleware = [
\App\Http\Middleware\ProfileRequests::class,
];
Conditional Profiling Use environment-based profiling:
if (app()->environment('production') && config('profiler.enabled')) {
Profiler::start('critical-path');
// ...
Profiler::stop();
}
Tagging and Metadata Attach metadata to profiles:
Profiler::start('user-action', [
'user_id' => auth()->id(),
'route' => route()->getName(),
]);
Batch Profiling Profile multiple actions in a single run:
Profiler::start('batch-job');
// Action 1
Profiler::snapshot('action-1');
// Action 2
Profiler::snapshot('action-2');
Profiler::stop();
Queue Job Profiling Profile long-running jobs:
namespace App\Jobs;
use Aeatech\SnapshotProfiler\Facades\Profiler;
use Illuminate\Bus\Queueable;
class ProcessData implements Queueable
{
public function handle()
{
Profiler::start('process-data-job');
// Job logic...
Profiler::stop();
}
}
XHProf Extension Missing
xhprof extension and restart PHP-FPM/web server.
pecl install xhprof
For Ubuntu/Debian:
sudo apt-get install php-xhprof
Storage Permissions
storage/app/profiler directory is writable:
mkdir -p storage/app/profiler
chmod -R 775 storage/app/profiler
Memory Overhead
Profiler::setSamplingRate(10); // Profile 1 in 10 calls
Incompatibility with OPcache
xhprof is loaded after OPcache in php.ini.Race Conditions in Multi-Threaded Environments
sync driver). Use database or redis drivers instead.Verify XHProf is Active Run a test script:
<?php
if (!function_exists('xhprof_enable')) {
die('XHProf is not enabled!');
}
xhprof_enable();
sleep(1);
$data = xhprof_disable();
file_put_contents('test.xhprof', serialize($data));
echo "Profile saved to test.xhprof";
If this fails, check phpinfo() for xhprof or consult your server admin.
Check Profile Files Inspect raw JSON output for errors:
cat storage/app/profiler/latest.json | jq
Use jq to validate structure.
Log Profiling Errors Add error handling:
try {
Profiler::start('test');
// ...
Profiler::stop();
} catch (\Exception $e) {
\Log::error('Profiling failed: ' . $e->getMessage());
}
Custom Storage Drivers
Extend Aeatech\SnapshotProfiler\Contracts\StorageDriver to save profiles to S3, databases, etc.:
namespace App\Profiler;
use Aeatech\SnapshotProfiler\Contracts\StorageDriver;
class S3StorageDriver implements StorageDriver
{
public function save(string $name, array $data): void
{
// Custom S3 logic
}
}
Register in config:
'storage' => \App\Profiler\S3StorageDriver::class,
Post-Processing Hooks Use events to process profiles after saving:
// In a service provider
Profiler::afterSave(function ($name, $data) {
// Send to monitoring system, etc.
});
Custom Metrics Extend the profiler to capture additional metrics (e.g., database queries):
Profiler::customMetric('db', 'query_time', 150);
UI Integration Build a custom dashboard using the raw JSON data:
$profile = Profiler::load('latest');
// Render with Blade or API
Profiler::setSamplingRate() to reduce overhead.Profiler::exclude(['/health', '/ping']);
Profiler::afterSave(function ($name, $data) {
dispatch(new ProcessProfileJob($name, $data));
});
How can I help you explore Laravel packages today?