laravel5-telescope maintained by hunglv-fs
Telescope for Laravel 5.0
laravel/telescope requires Laravel 5.7.7 and PHP 7.1. This gives a Laravel 5.0 application the
same thing: requests, queries, queue jobs, artisan commands, exceptions, logs, cache and mail, each
grouped into the batch that produced it — plus an Insights page that names your N+1 queries and
the statements burning the most time.
No npm, no build step, no Vue. The dashboard is plain Blade.
Why this exists
Telescope cannot be made to run on 5.0 by loosening version constraints. It is built on APIs that simply did not exist yet:
| laravel/telescope uses | Laravel 5.0 offers | What this package does |
|---|---|---|
Illuminate\Database\Events\QueryExecuted |
string event illuminate.query with four positional arguments |
QueryWatcher listens to the string event |
JobProcessing / JobProcessed / JobFailed |
only illuminate.queue.failed and illuminate.queue.stopping |
decorates the queue.worker binding |
CommandStarting / CommandFinished |
only artisan.start |
artisan.start plus a shutdown handler |
RequestHandled |
nothing | terminable middleware, prepended to the global stack |
Str::uuid() (ramsey/uuid) |
nothing | HungLv\Telescope\Support\Uuid |
| package auto-discovery | nothing | register the provider by hand |
| Gate / authorization | no Gate | Authorize middleware and Telescope::auth() |
| Vue SPA built with Laravel Mix | no build tooling | server-rendered Blade |
The database schema (telescope_entries, telescope_entries_tags) and the core concepts —
IncomingEntry, watchers, an entries repository, batch / tag / family hash — follow the original,
so what you learn here still applies when you eventually upgrade.
Requirements
- PHP 5.4 or newer
- Laravel 5.0.x
- A database connection (MySQL, PostgreSQL, SQLite — anything the query builder supports)
Verified against Laravel 5.0.14 on PHP 7.0. Laravel 5.1 to 5.6 are untested; several hooks still exist there, so it may well work, but nothing is promised.
Installation
composer require hunglv-fs/laravel5-telescope
Register the service provider in config/app.php:
'providers' => [
// ...
'HungLv\Telescope\TelescopeServiceProvider',
],
Publish the config file and the migration, then migrate:
php artisan vendor:publish --provider="HungLv\Telescope\TelescopeServiceProvider"
php artisan migrate
Open /telescope.
Drop the package somewhere such as packages/telescope and require its bundled autoloader from
bootstrap/autoload.php, right after Composer's:
require __DIR__.'/../packages/telescope/autoload.php';
Then copy config/telescope.php and the migration into your application by hand.
What gets recorded
| Watcher | Captures |
|---|---|
query |
SQL, bindings, duration, connection, the file:line in your app that issued it, slow and duplicate tags |
job |
name, queue, status, attempts, duration, peak memory, payload — with every query the job ran in the same batch |
command |
command line, duration, peak memory, query statistics, fatal errors |
request |
method, URI, action, status, duration, payload, headers, session, user, response |
exception |
class, message, file:line, stack trace, grouped by family hash |
log |
level, message, context |
cache |
hit, missed, set, forget |
mail |
subject, recipients, body |
event |
every string event (off by default — the 5.0 dispatcher is chatty) |
Everything a request, command or job does shares one batch, so opening any entry shows the whole timeline around it.
Debugging queue jobs
Each job processed by the worker becomes its own batch: the job entry plus every query, log line and
cache call it made. Open a job under /telescope/jobs to see how many queries it fired, how long it
took, and which statements repeated.
The job must run through queue.worker — php artisan queue:work or queue:listen. With the sync
driver the job simply runs inside the batch of whatever dispatched it, which is also what you want.
Insights
/telescope/insights answers the questions you actually open a profiler for:
- N+1 suspects — the same query shape executed several times inside one batch, with the
file:linethat issued it.in (?, ?)andin (?, ?, ?)are treated as one shape. - Query hotspots — recent queries grouped by shape, ranked by total time spent.
- Slowest requests, jobs and commands.
Configuration
Everything lives in config/telescope.php.
| Key | What it does |
|---|---|
enabled |
TELESCOPE_ENABLED=false registers no watcher at all |
path |
dashboard URI, default telescope |
limit |
maximum entries buffered per batch, so a runaway job cannot exhaust memory |
storage.database.connection |
null pins whatever database.default was at boot; set it to keep Telescope's writes out of your application's transactions |
ignore_paths |
Str::is() patterns; the dashboard itself is always ignored |
watchers.query.slow |
milliseconds before a query is tagged slow |
watchers.query.slow_only |
record slow queries only — the cheap way to run this outside development |
watchers.query.backtrace |
resolve the application frame that issued each query |
watchers.command.ignore |
commands that should not record themselves (queue:work, telescope:*, ...) |
local_environments, allowed_ips |
who may open the dashboard |
Restricting access
The dashboard shows request payloads, sessions and SQL. By default it only opens in the environments
listed under local_environments. To allow specific people elsewhere, register a callback in a
service provider's boot():
\HungLv\Telescope\Telescope::auth(function ($request) {
return in_array($request->user()->email, ['you@example.com']);
});
You can also drop entries before they are stored:
\HungLv\Telescope\Telescope::filter(function ($entry) {
return $entry->type !== 'query' || $entry->hasTag('slow');
});
And record a handled exception yourself:
try {
$this->import($row);
} catch (\Exception $e) {
\HungLv\Telescope\Telescope::catchException($e, ['import', 'row:'.$row->id]);
}
Pruning
php artisan telescope:prune --hours=48 # put this on cron
php artisan telescope:clear # delete everything
Laravel 5.0 quirks worth knowing
- Exceptions are recorded once. 5.0 binds
Psr\Log\LoggerInterfacestraight to$app['log']->getMonolog(), soHandler::report()bypassesIlluminate\Log\Writerand never firesilluminate.log. That is why the exception watcher decorates the handler binding instead of listening to the log event. - The storage connection is pinned at boot.
migrate --database=othercallssetDefaultConnection(), which rewritesdatabase.defaultglobally; resolving it lazily would silently send Telescope's own inserts to the wrong connection. - Closure jobs are broken on PHP 7 in Laravel 5.0 itself (SuperClosure 1.x pulls in a php-parser
release with a class named
String, reserved since PHP 7). Use'App\Foo@handle'or job objects.
Known limitations
- No live updates: the dashboard is a static Blade page, refresh to see new entries.
- No Redis, model, view or HTTP-client watchers — 5.0 emits no events for them.
- Artisan exit codes are not available (5.0 has no
CommandFinished); a command is reported asfailedon a fatal error, and taggedexceptionwhen one was reported. - No
telescope_monitoring(the "monitored tags" feature of the original).
Tests
Both run standalone, no application required:
php tests/smoke.php # recording pipeline: batching, tags, N+1 detection, caps, backtrace, row shape
composer install && php tests/views.php # compiles every view with the real Laravel 5.0 Blade compiler
Credits
An independent implementation for Laravel 5.0, following the schema and watcher architecture of laravel/telescope by Taylor Otwell. Not affiliated with or endorsed by Laravel.
License
MIT. See LICENSE.