spatie/backtrace
A cleaner, more useful PHP backtrace than debug_backtrace(). Create a Backtrace, get frames as typed Frame objects with correct file, line, class/method, and optional arguments, making stack traces easier to inspect, filter, and work with.
Install via Composer:
composer require spatie/backtrace
use Spatie\Backtrace\Backtrace;
// Get all frames as Frame objects
$frames = Backtrace::create()->frames();
// Access first frame properties
$firstFrame = $frames[0];
echo $firstFrame->file; // File path
echo $firstFrame->lineNumber; // Line number
echo $firstFrame->class; // Class name (null for functions)
use Spatie\Backtrace\Backtrace;
// Configure for Laravel
$backtrace = Backtrace::create()
->applicationPath(base_path())
->trimFilePaths();
// Get application frames only
$appFrames = $backtrace->frames();
// In a service or controller
public function handleRequest()
{
try {
// Business logic
} catch (\Exception $e) {
$backtrace = Backtrace::createForThrowable($e)
->withArguments()
->reduceArguments();
// Log or display enhanced backtrace
$this->logError($e, $backtrace->frames());
}
}
// Create custom reducer for complex objects
class UserArgumentReducer implements ArgumentReducer
{
public function execute($argument): ReducedArgumentContract
{
if (!$argument instanceof User) {
return UnReducedArgument::create();
}
return new ReducedArgument(
$argument->id . ' (' . $argument->email . ')',
get_class($argument)
);
}
}
// Apply in backtrace
$backtrace = Backtrace::create()
->withArguments()
->reduceArguments(
ArgumentReducers::default([
new UserArgumentReducer()
])
);
// Get only application frames (excluding vendor)
$appFrames = Backtrace::create()
->applicationPath(base_path())
->frames()
->filter(fn($frame) => $frame->applicationFrame);
// Display code context in error messages
$frame = Backtrace::create()->frames()[0];
$snippet = $frame->getSnippetAsString(5); // 5 lines context
// In Laravel's App\Exceptions\Handler
public function render($request, Throwable $exception)
{
$backtrace = Backtrace::createForThrowable($exception)
->withArguments()
->reduceArguments()
->applicationPath(base_path())
->trimFilePaths();
return response()->json([
'error' => $exception->getMessage(),
'trace' => $backtrace->frames()
]);
}
// In a Laravel command
protected function handle()
{
$backtrace = Backtrace::create()
->startingFromFrame(fn($frame) => $frame->class === self::class)
->limit(10);
$this->info('Execution trace:');
foreach ($backtrace->frames() as $frame) {
$this->line(sprintf(
'[%s:%d] %s::%s',
$frame->file,
$frame->lineNumber,
$frame->class ?? 'function',
$frame->method
));
}
}
Argument Collection Overhead:
withArguments() can significantly impact performanceAPP_DEBUG check:
if (app()->environment('local')) {
$backtrace->withArguments();
}
Vendor Frame Misclassification:
artisan and Statamic's please are treated as vendor filesFile Path Handling:
trimFilePaths() requires applicationPath() to be set firstrealpath() for consistency:
->applicationPath(realpath(base_path()))
Closure Arguments:
bindTo() for better visibilityPHP Configuration:
zend.exception_ignore_args must be 0 for throwable arguments.env:
zend.exception_ignore_args=0
Frame Inspection:
// Dump all frame properties
collect($frames)->each(function($frame) {
dd([
'file' => $frame->file,
'line' => $frame->lineNumber,
'class' => $frame->class,
'method' => $frame->method,
'args' => $frame->arguments,
'object' => $frame->object,
'app_frame' => $frame->applicationFrame
]);
});
Argument Reduction Debugging:
// Check which arguments are being reduced
$backtrace->reduceArguments(function($argument) {
return new ReducedArgument(
'REDACTED',
get_class($argument)
);
});
Performance Profiling:
// Benchmark backtrace generation
$start = microtime(true);
$frames = Backtrace::create()->frames();
$time = microtime(true) - $start;
Custom Frame Filtering:
// Filter frames by package
$vendorFrames = $backtrace->frames()->filter(function($frame) {
return str_starts_with($frame->file, base_path('vendor'));
});
Backtrace Comparison:
// Compare two backtraces
$original = Backtrace::create()->frames();
$modified = Backtrace::create()->frames();
$differences = collect($original)->zip($modified)
->filter(fn($pair) => $pair[0]->lineNumber !== $pair[1]->lineNumber)
->keys();
Backtrace Caching:
// Cache backtrace for expensive operations
$cacheKey = 'backtrace:'.md5($request->ip());
$frames = Cache::remember($cacheKey, now()->addMinutes(5), function() {
return Backtrace::create()->limit(20)->frames();
});
Integration with Laravel Scout:
// Use backtrace for search analytics
$searchBacktrace = Backtrace::create()
->startingFromFrame(fn($frame) => $frame->class === SearchRequest::class)
->limit(5);
SearchAnalytics::create([
'query' => $request->query,
'backtrace' => json_encode($searchBacktrace->frames()),
'user_id' => auth()->id()
]);
Application Path Detection:
vendor/ as application frames by default->applicationPath(base_path())
->customApplicationFrameDetector(function($file) {
return !str_contains($file, ['node_modules', 'bower_components']);
})
File Path Normalization:
->applicationPath(str_replace('\\', '/', base_path()))
Memory Management:
limit() to control size:
->limit(10) // Only keep most recent 10 frames
Custom Frame Properties:
// Add custom metadata to frames
$backtrace->frames()->each(function($frame) {
$frame->custom = [
'is_critical' => $frame->lineNumber > 100,
'package' => $this->detectPackage($frame->file)
];
});
Backtrace Decorators:
// Create reusable backtrace enhancers
class DebugDecorator
{
public function __invoke(Backtrace $backtrace)
{
return $backtrace
->withArguments()
->reduceArguments()
->applicationPath(base_path())
->trimFilePaths();
}
}
// Usage
$debugBacktrace = (new DebugDecorator())(Backtrace::create());
Backtrace Serialization:
// Convert backtrace to JSON
$serialized = $backtrace->frames()->map(function($frame) {
return [
'file' => $frame->file,
'line' => $frame->lineNumber,
'class' => $frame->class,
'method' => $frame->method,
'args' => $frame
How can I help you explore Laravel packages today?