Search by

wamesk / laravel-telescope-dashboard

WAME

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

Statistics

Installs: 132

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.5.0 2026-10-12 07:26 UTC

This package is auto-updated.

Last update: 2026-10-12 07:39:56 UTC


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 in telescope_entries.api_type for 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)
Mail 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 UPDATE per stored batch through the uuid index, 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_uri and api_type in chunks of sync_chunk rows and updates only the rows whose name changed.

  • Map changes are picked up automatically: after every php artisan migrate the package compares a fingerprint of the map with the last synced one (kept in the cache) and, when it differs, queues SyncApiTypesJob (unique, so parallel deploys run it once). The first migrate after installing the column backfills all existing entries this way. Disable it with TELESCOPE_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.