wamesk / laravel-telescope-dashboard
Advanced Telescope Dashboard with Vue 3 UI and type-specific filtering
Package info
github.com/wamesk/laravel-telescope-dashboard
Language:Vue
pkg:composer/wamesk/laravel-telescope-dashboard
Requires
- php: ^8.2
- laravel/framework: ^10.0|^11.0|^12.0|^13.0
- laravel/telescope: ^5.0
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Advanced replacement for the default Laravel Telescope UI. Built with Vue 3, Tailwind CSS, and type-specific filtering optimized with virtual generated columns for MySQL and MariaDB.
Features
- 18 entry types - Requests, Queries, Exceptions, Jobs, Logs, Mail, Events, Cache, Commands, Schedule, Models, Gates, Dumps, Notifications, Redis, Client Requests, Batches, Views
- Type-specific filters - Each entry type has its own set of relevant filters (HTTP method, status code, duration, log level, job status, etc.)
- API type column - Name Requests and Client Requests (e.g. by OpenAPI
operationId) from a per-project URL mask map, with a matching filter; the name is stored intelescope_entries.api_typefor plain SQL queries - Virtual column optimization - MySQL/MariaDB generated columns with composite indexes for fast filtering on large datasets
- Dark / Light mode - Theme toggle with localStorage persistence
- URL state sync - Filters and sorting are synced to URL query params for bookmarkable views
- Column sorting - Sort by sequence, created_at, duration, or query time with ASC/DESC toggle
- Inline & full-page detail - Expand entries in the table or open a full-page detail view with tabs
- Related entries - View all entries from the same batch grouped by type
- JSON viewer - Recursive viewer with expand/collapse and string truncation
- Copy to clipboard - Copy entry UUID or full JSON content
- Multi-language - English, Slovak, and Czech translations
- Authorization - Gate-based access control (default:
viewTelescope) - Responsive - Collapsible sidebar for mobile devices
Requirements
- PHP 8.2+
- Laravel 11.0+
- Laravel Telescope 5.0+
- MySQL 8.0+ or MariaDB 10.2+ (for virtual generated columns and JSON functions)
Installation
1. Install via Composer
composer require wamesk/laravel-telescope-dashboard
2. Publish assets
php artisan vendor:publish --tag=telescope-dashboard-assets
3. Run migrations
The package adds virtual generated columns and indexes to the telescope_entries table for optimized filtering:
php artisan migrate
4. (Optional) Publish configuration
php artisan vendor:publish --tag=telescope-dashboard-config
Configuration
After publishing, the config file is located at config/wame-telescope-dashboard.php:
return [ // Enable or disable the dashboard 'enabled' => env('TELESCOPE_DASHBOARD_ENABLED', true), // URL path for the dashboard 'path' => env('TELESCOPE_DASHBOARD_PATH', 'telescope-dashboard'), // Laravel gate used for authorization 'gate' => env('TELESCOPE_DASHBOARD_GATE', 'viewTelescope'), // Database connection for telescope data 'connection' => env('DB_CONNECTION_TELESCOPE', 'mysql_telescope'), // Default number of entries per page 'per_page' => 50, // Maximum entries per page (security limit) 'max_per_page' => 200, // Route group patterns for request filtering 'route_groups' => [ 'api' => '/api/v*', 'nova-api' => '/nova-api/*', 'web' => '/*', ], // Path to original Telescope (for "View in Telescope" links) 'telescope_path' => env('TELESCOPE_PATH', 'telescope'), // Middleware applied to all dashboard routes 'middleware' => ['web', 'auth'], ];
Authorization
The dashboard uses a Laravel gate for access control. By default, it checks the viewTelescope gate, which is typically defined by Telescope itself in app/Providers/TelescopeServiceProvider.php:
Gate::define('viewTelescope', function ($user) { return in_array($user->email, [ 'admin@example.com', ]); });
You can change the gate name via the gate config option or the TELESCOPE_DASHBOARD_GATE environment variable.
Usage
Once installed, visit your application at:
https://your-app.test/telescope-dashboard
Filtering
Each entry type provides its own set of filters. Examples:
| Entry Type | Available Filters |
|---|---|
| Requests | HTTP method, API type, URI, status code (2xx/3xx/4xx/5xx), min duration, route group, user email |
| Client Requests | HTTP method, API type, URI, status code (2xx/3xx/4xx/5xx), min duration |
| Queries | Slow queries (>100ms), query type (SELECT/INSERT/UPDATE/DELETE), min duration |
| Exceptions | Exception class |
| Jobs | Status (pending/completed/failed), job name |
| Logs | Level (emergency, alert, critical, error, warning, notice, info, debug) |
| Mailable class, recipient, subject | |
| Models | Action (created/updated/deleted), model type |
| Gates | Ability, result (allowed/denied) |
| Cache | Type (hit/missed/set/forget), key |
All entry types also support date range and content search filters.
Sorting
Entries can be sorted by:
- Sequence (default) - Insertion order
- Created at - Timestamp
- Duration - Request duration (requests only)
- Time - Query execution time (queries only)
URL State
All filter and sorting states are synced to URL query parameters. This means you can bookmark or share filtered views:
/telescope-dashboard/#/requests?methods=GET,POST&statuses=4xx,5xx&sort_by=c_duration&sort_direction=desc
API Type Column
Requests and Client Requests can show an API type column and filter, e.g. the OpenAPI operationId of the endpoint. The names come from a per-project map of URL masks; without a map the column and filter stay hidden.
Create config/telescope-api-types.php in the application:
return [ // Incoming requests (Telescope stores the path + query string) 'request' => [ 'GET /api/v1/location/autocomplete' => 'PostLocationAutocomplete', 'GET /api/v1/location/{placeId}' => 'GetLocationPoint', 'POST /api/v1/point/{pod}/meter/{sn}/deduction' => 'PostDeductionCreate', ], // Outgoing HTTP client requests (Telescope stores the full URL) 'client_request' => [ 'GET */api/v1/pods/{podNumber}/meters' => 'Billien7.getPodMeters', 'POST */api/v1/pods' => 'Billien7.getPointsOfDeliveryDetail', ], ];
Mask syntax (key = "METHOD mask", value = display name):
| Part | Meaning |
|---|---|
GET prefix |
Optional HTTP method; without it the mask matches any method |
{param} |
Exactly one path segment |
* |
Anything up to the query string (e.g. the scheme and host of outgoing requests) |
no ? in the mask |
The query string is ignored |
?key=* in the mask |
The query string is matched too; * there matches anything |
- The first matching mask wins — keep literal segments above
{param}masks. - Several masks may share one name; the filter (sorted alphabetically) then groups them.
- Unmatched entries show
-; the filter option Unmatched lists them (handy to find endpoints missing from the map).
Stored api_type column
The resolved name is stored in the telescope_entries.api_type column (VARCHAR(191), NULL when no mask matches, index idx_te_api_type), so it can be queried directly:
SELECT api_type, COUNT(*) AS requests, AVG(c_duration) AS avg_ms FROM telescope_entries WHERE type = 'request' AND created_at >= NOW() - INTERVAL 1 DAY GROUP BY api_type ORDER BY requests DESC;
The dashboard column and filter read the same column (the filter is an indexed equality instead of a REGEXP per row).
-
New entries get their name right after Telescope stores them (one
UPDATEper stored batch through theuuidindex, only when the batch holds a matching request / client request). Telescope stores entries while the application terminates, after the response was sent. -
Existing entries are recomputed by:
php artisan telescope-dashboard:sync-api-types # only when the map changed since the last sync php artisan telescope-dashboard:sync-api-types --force # always
It reads only
sequence,c_method,c_uriandapi_typein chunks ofsync_chunkrows and updates only the rows whose name changed. -
Map changes are picked up automatically: after every
php artisan migratethe package compares a fingerprint of the map with the last synced one (kept in the cache) and, when it differs, queuesSyncApiTypesJob(unique, so parallel deploys run it once). The firstmigrateafter installing the column backfills all existing entries this way. Disable it withTELESCOPE_DASHBOARD_API_TYPES_SYNC_AFTER_MIGRATE=false. -
A provider whose map changes at runtime (e.g. read from a database) can schedule the command instead; it exits right away while the map is unchanged:
// routes/console.php Schedule::command('telescope-dashboard:sync-api-types')->hourly();
// config/wame-telescope-dashboard.php 'api_types' => [ 'provider' => ConfigApiTypeProvider::class, 'config_key' => 'telescope-api-types', 'sync_after_migrate' => env('TELESCOPE_DASHBOARD_API_TYPES_SYNC_AFTER_MIGRATE', true), 'sync_queue' => [ 'connection' => env('TELESCOPE_DASHBOARD_API_TYPES_SYNC_CONNECTION'), 'queue' => env('TELESCOPE_DASHBOARD_API_TYPES_SYNC_QUEUE'), ], 'sync_chunk' => 1000, 'cache_store' => null, ],
Custom provider
The map is read by ConfigApiTypeProvider. To load it from elsewhere (a database, a parsed OpenAPI file, ...), implement Wame\LaravelTelescopeDashboard\Contracts\ApiTypeProvider and point the config at it:
// config/wame-telescope-dashboard.php 'api_types' => [ 'provider' => App\Telescope\OpenApiTypeProvider::class, 'config_key' => 'telescope-api-types', ],
Database Optimization
The package adds virtual generated columns (compatible with MySQL 8.0+ and MariaDB 10.2+) to the telescope_entries table for efficient filtering without modifying stored data:
| Column | Source | Type |
|---|---|---|
c_method |
content->$.method |
VARCHAR(10) |
c_uri |
content->$.uri |
VARCHAR(500) |
c_response_status |
content->$.response_status |
SMALLINT UNSIGNED |
c_duration |
content->$.duration |
DECIMAL(10,2) |
c_time |
content->$.time |
DECIMAL(10,2) |
Each virtual column has a composite index with (type, should_display_on_index, <column>) for optimal query performance.
The stored (not virtual) api_type column holds the resolved API type name and is indexed as (api_type, type, should_display_on_index), see Stored api_type column.
API Endpoints
The dashboard exposes the following JSON API endpoints (all protected by auth + gate middleware):
| Method | Endpoint | Description |
|---|---|---|
POST |
/telescope-dashboard/api/entries |
Search entries with filters and sorting |
GET |
/telescope-dashboard/api/entries/{uuid} |
Get a single entry |
GET |
/telescope-dashboard/api/entries/{uuid}/detail |
Get entry with related batch entries |
GET |
/telescope-dashboard/api/filters/{type} |
Get available filter values for an entry type |
Frontend Development
To modify the Vue 3 frontend:
cd wamesk/laravel-telescope-dashboard # Install dependencies npm install # Start dev server with HMR npm run dev # Build for production npm run build
The frontend stack:
- Vue 3.4+ with Composition API
- Vue Router 4.3+ (hash mode)
- Tailwind CSS 3.4+
- Vite 5.0+
After building, publish updated assets:
php artisan vendor:publish --tag=telescope-dashboard-assets --force
Translations
The package ships with English, Slovak, and Czech translations. To publish and customize:
php artisan vendor:publish --tag=telescope-dashboard-translations
Translation files are located in resources/lang/{en,sk,cz}/telescope-dashboard.php.
Comparison with Default Telescope UI
| Feature | Default Telescope | This Package |
|---|---|---|
| UI framework | Blade templates | Vue 3 SPA |
| Filtering | Basic | Type-specific with 18 filter variants |
| Sorting | Sequence only | Sequence, created_at, duration, time |
| Query performance | Direct JSON queries | Virtual columns + composite indexes (MySQL & MariaDB) |
| Theme | Light only | Dark / Light with toggle |
| URL state | None | Full filter/sort sync |
| Entry details | Separate page | Inline expand + full-page detail |
| Batch viewing | Flat list | Grouped by entry type |
| Copy support | None | UUID and JSON copy |
| Responsive | Desktop-focused | Mobile-friendly |
| Translations | English only | EN, SK, CZ |
License
The MIT License (MIT). Please see License File for more information.