VOOZH about

URL: https://deepwiki.com/hypervel/telescope/2.5-data-storage

⇱ Data Storage | hypervel/telescope | DeepWiki


Loading...
Last indexed: 7 February 2026 (146f77)
Menu

Data Storage

Telescope's storage layer persists monitoring entries captured by watchers into a relational database. The architecture employs the repository pattern to abstract storage operations, enabling potential alternative backends while maintaining a consistent interface throughout the system.

This page documents the three-table database schema (telescope_entries, telescope_entries_tags, telescope_monitoring), the repository contract hierarchy (EntriesRepository, ClearableRepository, PrunableRepository), and the concrete DatabaseEntriesRepository implementation.

For entry lifecycle and deferred storage mechanisms, see page 2.1.3 (Storage Lifecycle Management). For entry data structures (IncomingEntry, EntryResult), see page 2.2 (Data Models).

Storage Architecture Overview

Telescope uses a repository pattern to abstract storage operations from the rest of the system. The storage layer consists of three primary components:

  1. Repository Contracts - Interface definitions for storage operations
  2. Database Implementation - Concrete implementation using SQL database
  3. Schema Design - Three normalized tables for entries, tags, and monitoring configuration

Storage Layer Architecture


Sources: src/TelescopeServiceProvider.php163-179 database/migrations/2025_02_08_000000_create_telescope_entries_table.php24-56

Database Schema

The storage layer uses three tables to organize monitoring data. The migration file defines the complete schema structure.

telescope_entries Table

The primary table stores all monitoring entries captured by watchers. Each row represents a single monitored event (request, query, cache hit, exception, etc.).

Schema Definition:

ColumnBlueprint MethodTypeConstraintsPurpose
sequencebigIncrements()BIGINT UNSIGNEDPrimary Key, Auto-incrementSequential identifier for ordering
uuiduuid()CHAR(36)Unique indexGlobally unique entry identifier (UUID v4)
batch_iduuid()CHAR(36)IndexedGroups entries from same request/command/job execution
family_hashstring()VARCHAR(255)Indexed, NullableGroups similar queries/requests for aggregation (e.g., same SQL pattern)
should_display_on_indexboolean()TINYINT(1)Default: true, Composite indexControls visibility in dashboard - set to false for filtered entries
typestring(20)VARCHAR(20)Composite indexEntry type discriminator: 'request', 'query', 'cache', 'exception', etc.
contentlongText()LONGTEXT-JSON-encoded entry payload containing all captured data
created_atdateTime()DATETIMEIndexed, NullableTimestamp of entry creation (when watcher recorded it)

Index Strategy:

IndexColumnsPurpose
uuid (unique)uuidDirect entry lookup by identifier
batch_idbatch_idRetrieve all entries in a request/command/job batch
family_hashfamily_hashGroup similar queries/requests for aggregate analysis
created_atcreated_atTime-range queries and prune() operations
type, should_display_on_indextype, should_display_on_indexDashboard filtering by entry type (only visible entries)

Sources: database/migrations/2025_02_08_000000_create_telescope_entries_table.php28-43

telescope_entries_tags Table

Junction table implementing a many-to-many relationship between entries and tags. Tags enable filtering by user ID, job class, controller name, exception type, or any custom categorization.

Schema Definition:

ColumnBlueprint MethodTypeConstraintsPurpose
entry_uuiduuid()CHAR(36)Composite PK, FK to telescope_entries.uuidReferences the tagged entry
tagstring()VARCHAR(255)Composite PK, IndexedTag value (e.g., App\Jobs\ProcessPodcast, User:123)

Index Strategy:

IndexColumnsPurpose
PRIMARYentry_uuid, tagPrevents duplicate tags per entry; enforces uniqueness
tagtagEnables efficient filtering: "show me all entries tagged with User:123"

Tag Format Examples:

  • User:{id} - Entries associated with specific user
  • {JobClass} - Queue job entries by class name
  • {ControllerClass}:{method} - HTTP requests by controller action
  • {ExceptionClass} - Exception entries by exception type

Sources: database/migrations/2025_02_08_000000_create_telescope_entries_table.php45-51

telescope_monitoring Table

Stores tags for active monitoring. When a tag exists in this table, the DatabaseEntriesRepository sets should_display_on_index = true for all entries with that tag during storage, overriding normal filtering logic.

Schema Definition:

ColumnBlueprint MethodTypeConstraintsPurpose
tagstring()VARCHAR(255)Primary KeyTag value to monitor (e.g., User:123)

Usage Pattern:

// Enable monitoring for specific user
INSERT INTO telescope_monitoring (tag) VALUES ('User:123');

// All entries tagged with 'User:123' now bypass filters
// and appear in dashboard regardless of path/command filters

This table enables runtime monitoring control without configuration changes. Common use cases:

  • Monitor specific user's activity for debugging
  • Track all requests for a problematic job class
  • Watch all exceptions of a specific type

Sources: database/migrations/2025_02_08_000000_create_telescope_entries_table.php53-55

Entity Relationship Diagram


Relationship Details:

  • telescope_entriestelescope_entries_tags: One-to-many (one entry can have multiple tags)
  • telescope_monitoringtelescope_entries_tags: Lookup relationship (monitoring table drives filtering logic in repository)

Sources: database/migrations/2025_02_08_000000_create_telescope_entries_table.php24-56

Repository Pattern

Telescope uses the repository pattern to provide an abstraction layer between the application logic and the data storage mechanism. This design allows for potential alternative storage backends (Redis, Elasticsearch, etc.) without modifying the core system.

Repository Contracts

Telescope defines three repository interfaces that separate concerns for storage operations. The DatabaseEntriesRepository class implements all three contracts, providing a unified SQL-based implementation.

EntriesRepository Contract

Located at src/Contracts/EntriesRepository.php, this interface defines core CRUD operations for monitoring entries.

Method Signatures:

MethodParametersReturn TypePurpose
store()Collection $entriesCollectionPersist batch of IncomingEntry objects; returns pending updates
update()Collection $updatesvoidApply updates to existing entries (e.g., add exception context to request)
get()string $type, EntryQueryOptions $optionsCollectionRetrieve entries by type with filtering (limit, tag, family_hash)
find()string $idEntryResult|nullFetch single entry by UUID
monitoring()nonearrayReturn list of monitored tags from telescope_monitoring table

The store() method is called from Telescope::store() at the end of each request/command/job execution. It returns a collection of updates that require asynchronous processing (dispatched to ProcessPendingUpdates job).

Sources: src/TelescopeServiceProvider.php166-168

ClearableRepository Contract

Located at src/Contracts/ClearableRepository.php, this interface provides data wiping capability.

Method Signatures:

MethodParametersReturn TypePurpose
clear()nonevoidDelete all entries from storage (TRUNCATE tables)

Consumed by Console\ClearCommand::handle() to implement the telescope:clear command.

Sources: src/TelescopeServiceProvider.php170-173

PrunableRepository Contract

Located at src/Contracts/PrunableRepository.php, this interface handles data retention and cleanup.

Method Signatures:

MethodParametersReturn TypePurpose
prune()DateTimeInterface $beforeintDelete entries older than specified date; returns count deleted
pruneExceptions()DateTimeInterface $beforeintDelete only exception entries older than specified date

Consumed by Console\PruneCommand::handle() to implement the telescope:prune command with configurable retention policies.

Sources: src/TelescopeServiceProvider.php175-178

DatabaseEntriesRepository Implementation

The DatabaseEntriesRepository class at src/Storage/DatabaseEntriesRepository.php implements all three repository contracts, providing a unified SQL-based storage implementation using Hyperf's database abstraction layer.

Repository Method to SQL Operation Mapping


Key Implementation Details:

OperationImplementation Strategy
store()Batch inserts entries and tags; checks telescope_monitoring to set should_display_on_index; uses chunking (configurable via telescope.storage.database.chunk)
update()Updates content field for existing entries by UUID
get()LEFT JOIN with telescope_entries_tags; filters by type, tags, family_hash, date range; applies monitoring logic
find()Direct SELECT by UUID; returns EntryResult or null
clear()TRUNCATE both tables (fast deletion for maintenance)
prune()DELETE with date filter; cascades to tags via foreign key or explicit deletion

The implementation also handles the TerminableRepository interface (if implemented), calling terminate() after storage to perform cleanup operations.

Sources: src/TelescopeServiceProvider.php163-179

Storage Driver Registration

The TelescopeServiceProvider registers the storage driver during the service container registration phase. This process binds the repository contracts to their concrete implementations.

Storage Driver Registration

The TelescopeServiceProvider registers storage drivers during the register() phase using a convention-based approach that enables extensibility.

Registration Process


Convention-Based Driver Resolution:

The method at src/TelescopeServiceProvider.php151-158 implements driver registration:

  1. Read telescope.driver configuration (default: 'database')
  2. Build method name: 'register' . ucfirst($driver) . 'Driver''registerDatabaseDriver'
  3. If method exists on provider class, invoke it to bind contracts

This pattern enables custom storage drivers without modifying core code. To add a Redis driver, implement registerRedisDriver() and set config('telescope.driver', 'redis').

Database Driver Bindings:

The registerDatabaseDriver() method at src/TelescopeServiceProvider.php163-179 creates three bindings:

ContractImplementationUsage
EntriesRepository::classDatabaseEntriesRepository::classCore storage operations (used by Telescope::store())
ClearableRepository::classDatabaseEntriesRepository::classData wiping (used by Console\ClearCommand)
PrunableRepository::classDatabaseEntriesRepository::classData retention (used by Console\PruneCommand)

This design decouples consumers from implementation details. Commands type-hint specific contracts rather than concrete classes, enabling potential future implementations (e.g., read-only repository, distributed storage).

Sources: src/TelescopeServiceProvider.php151-158 src/TelescopeServiceProvider.php163-179

Storage Configuration

Storage behavior is controlled through the config/telescope.php configuration file. The service provider respects these settings when initializing the storage layer.

Storage Configuration

Storage behavior is controlled through config/telescope.php. Key settings affect database connections, batching, and driver selection.

Database Connection Configuration

The migration class at database/migrations/2025_02_08_000000_create_telescope_entries_table.php14-19 dynamically resolves the database connection:


This enables Telescope data isolation in a separate database from application data.

Storage Configuration Options

Configuration KeyTypeDefaultPurpose
telescope.driverstring'database'Storage driver to use (resolved by registerStorageDriver())
telescope.storage.database.connectionstring|nullnullDatabase connection name (null = default connection)
telescope.storage.database.chunkint1000Batch size for INSERT operations in DatabaseEntriesRepository

Example Configuration:


Connection Resolution Priority:

  1. config('telescope.storage.database.connection') (if set)
  2. Default database connection (from config/database.php)

For deferred storage configuration (telescope.defer, telescope.queue), see page 4.1 (Core Settings).

Sources: database/migrations/2025_02_08_000000_create_telescope_entries_table.php14-19 src/TelescopeServiceProvider.php153

Data Flow Through Storage Layer

The following diagram illustrates how data moves from watchers through the Telescope facade to the repository layer and finally to the database:


Sources: src/Watchers/CacheWatcher.php65-69 src/TelescopeServiceProvider.php163-179

This batched approach minimizes database round-trips by collecting all entries from a request/command/job and persisting them in a single operation at the end of the lifecycle.