VOOZH about

URL: https://deepwiki.com/hypervel/telescope/2-architecture

⇱ Architecture | hypervel/telescope | DeepWiki


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

Architecture

This document provides a detailed explanation of Telescope's architectural design, including its layered structure, component relationships, core design patterns, and data flow. It covers the overall system organization and how the major components interact to provide observability for Hypervel applications.

For detailed documentation of individual core components, see Core Components. For watcher implementations, see Watchers System. For storage implementation details, see Data Storage. For framework integration specifics, see Framework Integration.

Overview

Telescope's architecture is organized into six distinct layers that work together to capture, process, and store application telemetry data. The system employs a static facade pattern (Telescope class), 18+ specialized watchers, and coroutine-safe context management to provide comprehensive, non-intrusive observability for Hypervel applications built on Hyperf framework.

Key Architectural Patterns:

  • Static Facade: Telescope class provides centralized API for entry recording
  • Plugin Architecture: Modular watcher system with independent enable/disable controls
  • Repository Pattern: Abstracted storage layer with interface contracts
  • Context-Based State: Coroutine-safe state management via Hyperf\Context
  • Event-Driven Storage: Lifecycle event hooks trigger deferred persistence
  • Dual-Provider Integration: ConfigProvider for Hyperf DI container, TelescopeServiceProvider for Hypervel service registration

Sources: src/Telescope.php29-750 src/TelescopeServiceProvider.php18-180 src/ListensForStorageOpportunities.php21-177

Architectural Layers

Layer 1: Application Layer

The Hypervel application generates events during normal operation. Telescope captures these events without requiring application code modifications.

Event Sources:

  • HTTP request/response cycles via RequestReceived event
  • Console command executions via BeforeHandleCommand and AfterExecuteCommand events
  • Queue job processing via JobProcessing, JobProcessed, JobFailed events
  • Scheduled task runs via Hyperf's scheduler
  • Database queries via database event dispatcher
  • Cache operations via CacheHit, CacheMissed, KeyWritten, KeyForgotten events
  • Other application-level events (mail sent, exceptions, etc.)

Sources: src/ListensForStorageOpportunities.php60-176 src/Watchers/CacheWatcher.php36-39

Layer 2: Core Layer

The core layer coordinates all Telescope operations through three components:

ComponentFilePrimary MethodsRole
TelescopeServiceProvidersrc/TelescopeServiceProvider.phpboot(), register(), registerStorageDriver()Bootstrap, watcher registration, route setup, repository binding
Telescopesrc/Telescope.phpstart(), record(), store(), isRecording()Static facade, recording API, filtering, storage coordination
Context Systemsrc/Telescope.php36-46Context::get(), Context::set(), Context::override()Coroutine-safe state management via context keys

Context Keys (constants):

KeyTypePurposeSet At
ENTRIES_QUEUEarrayAccumulates IncomingEntry objects awaiting storagesrc/Telescope.php308-310
UPDATES_QUEUEarrayAccumulates EntryUpdate objects for deferred updatessrc/Telescope.php346-348
BATCH_IDstringUUID grouping related entries within a request/jobsrc/Telescope.php665-668
SHOULD_RECORDboolControls whether recording is activesrc/Telescope.php231
IS_RECORDINGboolPrevents recursive recording during storagesrc/Telescope.php292
HAS_STOREDboolTracks if defer() callback has been registeredsrc/Telescope.php289

Sources: src/Telescope.php36-46 src/Telescope.php216-319 src/TelescopeServiceProvider.php23-122

Layer 3: Watcher Layer

Telescope includes 18+ specialized watchers that implement a consistent plugin architecture. Each watcher monitors a specific application aspect through event listening and method interception.

Watcher Execution Pattern:

  1. Registers event listeners via EventDispatcherInterface->listen() in its register() method
  2. Checks Telescope::isRecording() before processing events
  3. Extracts relevant data from events
  4. Creates IncomingEntry objects via IncomingEntry::make()
  5. Calls type-specific Telescope::recordX() methods

Complete Watcher Catalog:

CategoryWatchersEntry TypeMonitoring Target
HTTPRequestWatcher, HttpClientWatcher, ClientRequestWatcherREQUEST, CLIENT_REQUESTHTTP requests/responses, outgoing HTTP calls
DatabaseQueryWatcher, ModelWatcher, RedisWatcherQUERY, MODEL, REDISSQL queries, Eloquent events, Redis commands
BackgroundJobWatcher, ScheduleWatcher, BatchWatcherJOB, SCHEDULED_TASK, BATCHQueue jobs, scheduled tasks, batch jobs
ApplicationEventWatcher, LogWatcher, ExceptionWatcher, CommandWatcherEVENT, LOG, EXCEPTION, COMMANDApplication events, logs, exceptions, console commands
PerformanceCacheWatcher, ViewWatcher, DumpWatcherCACHE, VIEW, DUMPCache operations, view rendering, debug dumps
CommunicationMailWatcher, NotificationWatcherMAIL, NOTIFICATIONSent emails, dispatched notifications
AuthorizationGateWatcherGATEAuthorization gate checks

Watcher Configuration Structure:


Each watcher can be independently enabled/disabled via configuration keys or environment variables (e.g., TELESCOPE_CACHE_WATCHER).

Sources: src/Watchers/CacheWatcher.php18-156 src/TelescopeServiceProvider.php119-146 src/Telescope.php362-493 config/telescope.php

Layer 4: AOP Integration

Aspect-Oriented Programming enables non-intrusive interception for components that don't emit events. Implemented via Hyperf's AOP system:

Current Implementation:

  • GuzzleHttpClientAspect weaves GuzzleHttp\Client::transfer() method
  • Extracts request/response data via $proceedingJoinPoint->process() interception
  • Delegates to HttpClientWatcher for entry creation and recording
  • Configured automatically when HttpClientWatcher is enabled

The aspect pattern allows monitoring external HTTP requests without modifying application code or requiring event dispatching.

Sources: Referenced in high-level system diagrams, implemented in aspect classes not shown in provided files

Layer 5: Storage Layer

The storage layer implements deferred persistence with queue-based updates:

Components:

ComponentTypeMethodsPurpose
ListensForStorageOpportunitiesTraitrecordEntriesForRequests(), manageRecordingStateForCommands(), storeEntriesAfterWorkerLoop()Registers lifecycle event listeners to trigger storage
EntriesRepositoryInterfacestore(), update()Defines storage contract
DatabaseEntriesRepositoryClassstore(), update()MySQL/PostgreSQL implementation
ProcessPendingUpdatesQueue Jobhandle()Async entry updates

Storage Trigger Points:

ContextTrigger EventCode Location
HTTP RequestsCoroutine termination via defer()src/Telescope.php286-289 src/ListensForStorageOpportunities.php63-73
Console CommandsAfterExecuteCommand eventsrc/ListensForStorageOpportunities.php88-93
Queue JobsJobProcessed or JobFailed events (when job stack is empty)src/ListensForStorageOpportunities.php143-175

Sources: src/ListensForStorageOpportunities.php21-177 src/Telescope.php579-637

Layer 6: Data Layer

The persistence layer consists of three database tables and asynchronous update processing:

Database Schema:

TablePrimary KeyPurposeKey Columns
telescope_entriessequence (bigint auto-increment)Main entry storageuuid, batch_id, type, content, family_hash, created_at
telescope_entries_tagsComposite: entry_uuid + tagMany-to-many entry taggingentry_uuid, tag
telescope_monitoringtagActive monitoring tagstag

Indices:

  • telescope_entries: uuid (unique), batch_id, family_hash, created_at, [type, should_display_on_index]
  • telescope_entries_tags: tag

Background Processing:

  • ProcessPendingUpdates queue job handles deferred entry updates asynchronously
  • Dispatched by DatabaseEntriesRepository::store() after initial insertion
  • Uses configured queue connection and queue name from config('telescope.queue')

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

Data Flow Architecture

Telescope's data collection pipeline consists of three distinct phases: generation, queueing, and storage. The following diagram illustrates the complete lifecycle of a Telescope entry from event occurrence to database persistence:

Three-Phase Data Collection Pipeline


Phase Breakdown:

PhaseTriggerOperationsContext Keys Modified
1. GenerationApplication event firesWatcher checks isRecording(), extracts data, creates IncomingEntryNone (read-only)
2. QueueingTelescope::recordX() calledApply user context, tags, filters; append to queue; register defer callback onceENTRIES_QUEUE, HAS_STORED, IS_RECORDING
3. StorageLifecycle event (deferred)Apply batch filters, assign BATCH_ID, persist via repository, dispatch updates jobBATCH_ID, clear ENTRIES_QUEUE and UPDATES_QUEUE

Critical Flow Characteristics:

  1. Re-entrancy Guard: IS_RECORDING flag src/Telescope.php281-283 prevents infinite loops when Telescope's own operations trigger watchers
  2. Single Defer Registration: HAS_STORED flag src/Telescope.php285-290 ensures defer() callback is registered only once per batch
  3. Two-Tier Filtering: Individual filtering via filterUsing src/Telescope.php307 batch filtering via filterBatchUsing src/Telescope.php599
  4. Batch Grouping: All entries in a request/command/job share a UUID via BATCH_ID src/Telescope.php665-668
  5. Deferred Storage: Uses Hyperf's defer() function src/Telescope.php286 to postpone persistence until coroutine termination
  6. Async Updates: Complex updates dispatched to ProcessPendingUpdates job src/Telescope.php612 for background processing

Sources: src/Telescope.php275-319 src/Telescope.php579-637 src/ListensForStorageOpportunities.php63-176

Core Design Patterns

Static Facade Pattern

The Telescope class provides a static API for recording entries without requiring instance management. This pattern enables watchers to record entries via simple static method calls.

Facade Method Delegation Flow


Complete Recording API:

MethodLine RangeEntry Type ConstantUse Case
recordBatch()src/Telescope.php354-357EntryType::BATCHJob batch operations
recordCache()src/Telescope.php362-365EntryType::CACHECache hits/misses/writes
recordCommand()src/Telescope.php370-373EntryType::COMMANDConsole command execution
recordDump()src/Telescope.php378-381EntryType::DUMPDebug dump output
recordEvent()src/Telescope.php386-389EntryType::EVENTApplication events
recordException()src/Telescope.php394-397EntryType::EXCEPTIONThrown exceptions
recordGate()src/Telescope.php402-405EntryType::GATEAuthorization checks
recordJob()src/Telescope.php410-413EntryType::JOBQueue job execution
recordLog()src/Telescope.php418-421EntryType::LOGLog messages
recordMail()src/Telescope.php426-429EntryType::MAILSent emails
recordNotification()src/Telescope.php434-437EntryType::NOTIFICATIONDispatched notifications
recordQuery()src/Telescope.php442-445EntryType::QUERYDatabase queries
recordModelEvent()src/Telescope.php450-453EntryType::MODELEloquent model events
recordRedis()src/Telescope.php458-461EntryType::REDISRedis commands
recordRequest()src/Telescope.php466-469EntryType::REQUESTHTTP requests
recordScheduledCommand()src/Telescope.php474-477EntryType::SCHEDULED_TASKScheduled tasks
recordView()src/Telescope.php482-485EntryType::VIEWView rendering
recordClientRequest()src/Telescope.php490-493EntryType::CLIENT_REQUESTOutgoing HTTP requests

All 18 recording methods delegate to protected static record(string $type, IncomingEntry $entry) src/Telescope.php275-319 which handles filtering, tagging, and queueing.

Sources: src/Telescope.php29-750 src/Telescope.php275-319 src/Telescope.php354-493

Repository Pattern

Storage abstraction through interface contracts enables swapping storage backends without modifying core logic. The current implementation uses database storage, but the interface design allows for alternative backends (Redis, Elasticsearch, etc.).

Repository Interface Hierarchy:

InterfaceMethod ContractPurposeBound To
EntriesRepositorystore(Collection $entries), update(Collection $updates)Core persistence operationsDatabaseEntriesRepository
ClearableRepositoryclear()Bulk deletion of all entriesDatabaseEntriesRepository
PrunableRepositoryprune(DateTimeInterface $before)Time-based entry cleanupDatabaseEntriesRepository
TerminableRepositoryterminate()Post-storage cleanup (optional)DatabaseEntriesRepository

DI Container Binding:


The repository is resolved from the container at runtime src/Telescope.php134 and used throughout the storage lifecycle src/Telescope.php596-637

Sources: src/TelescopeServiceProvider.php151-178 src/Telescope.php134 src/Telescope.php596-637

Context-Based State Management

Telescope leverages Hyperf's Context system for coroutine-safe state management. This is critical in Swoole-based applications where multiple requests execute concurrently in isolated coroutines.

Context Key Design:

ConstantTypeIsolation LevelPurpose
ENTRIES_QUEUEarrayCoroutine-scopedAccumulates IncomingEntry objects
UPDATES_QUEUEarrayCoroutine-scopedAccumulates EntryUpdate objects
BATCH_IDstringCoroutine-scoped, parent-propagatedUUID for grouping related entries
SHOULD_RECORDboolCoroutine-scoped, parent-propagatedControls recording state
IS_RECORDINGboolCoroutine-scoped, parent-propagatedPrevents recursive recording
HAS_STOREDboolCoroutine-scopedTracks defer callback registration

Coroutine Context Propagation:


Implementation in TelescopeServiceProvider:


Propagation Benefits:

  • Async HTTP client calls maintain batch association with parent request
  • Background jobs spawned during request inherit recording context
  • Parallel database queries are properly grouped in Telescope UI
  • Context isolation prevents cross-request data contamination

Sources: src/Telescope.php36-46 src/TelescopeServiceProvider.php38-47 src/Telescope.php665-668

Event-Driven Storage Lifecycle

Storage operations are triggered by framework lifecycle events rather than immediate writes, ensuring entries are grouped by execution context and persisted only after operations complete.

Storage Trigger Mechanisms by Execution Context:

ContextStart TriggerEnd TriggerStorage Method
HTTP RequestRequestReceived eventCoroutine defer() callbackAutomatic at coroutine termination
Console CommandBeforeHandleCommand eventAfterExecuteCommand eventEvent-triggered
Queue JobJobProcessing event (push to stack)JobProcessed/JobFailed event (pop stack, store if empty)Event-triggered with stack tracking

HTTP Request Lifecycle:


Console Command Lifecycle:


Queue Job Lifecycle with Stack Tracking:


The job stack ($jobsProcessing array) prevents premature storage when nested jobs are executing, ensuring all entries from a job tree share the same batch.

Sources: src/ListensForStorageOpportunities.php33-176 src/Telescope.php286-289 src/Telescope.php216-242

Plugin Architecture for Watchers

Watchers follow a consistent registration and execution pattern:

Watcher Lifecycle:


Configuration Structure:


Each watcher:

  1. Checks if recording is active before processing
  2. Extracts relevant data from the event
  3. Creates an IncomingEntry with structured data
  4. Calls the appropriate Telescope::recordX() method

Sources: src/Watchers/CacheWatcher.php28-40 src/Watchers/CacheWatcher.php59-70

Component Interaction Map

The following diagram bridges natural language system descriptions to actual code entities by showing method calls and data flows:


Code Entity Reference Map:

SubsystemPrimary Class/TraitKey MethodsLine References
BootstrapTelescopeServiceProviderboot(), register(), registerStorageDriver()src/TelescopeServiceProvider.php23-48 src/TelescopeServiceProvider.php112-122 src/TelescopeServiceProvider.php151-178
Core FacadeTelescopestart(), record(), store(), isRecording()src/Telescope.php123-135 src/Telescope.php275-319 src/Telescope.php579-637 src/Telescope.php263-270
Watcher RegistryTelescope (uses RegistersWatchers trait)registerWatchers()src/Telescope.php34
Storage LifecycleTelescope (uses ListensForStorageOpportunities trait)listenForStorageOpportunities(), recordEntriesForRequests(), manageRecordingStateForCommands(), storeEntriesAfterWorkerLoop()src/Telescope.php33 src/ListensForStorageOpportunities.php38-107
Context ManagementTelescope + Context (Hyperf)getBatchId(), startRecording(), stopRecording()src/Telescope.php665-668 src/Telescope.php216-234 src/Telescope.php239-242
Repository LayerDatabaseEntriesRepositorystore(), update()Referenced in interface contracts
Background JobsProcessPendingUpdateshandle()src/Telescope.php612

Context Key Operations:

Context KeyRead OperationsWrite OperationsCritical Usage Points
ENTRIES_QUEUETelescope::getEntriesQueue() src/Telescope.php324-327Telescope::record() via Context::override() src/Telescope.php308-310Entry accumulation, batch storage
UPDATES_QUEUETelescope::getUpdatesQueue() src/Telescope.php332-335Telescope::recordUpdate() via Context::override() src/Telescope.php346-348Deferred entry updates
BATCH_IDTelescope::executeStore() src/Telescope.php604Telescope::getBatchId() via Context::getOrSet() src/Telescope.php667Entry grouping
SHOULD_RECORDTelescope::isRecording() src/Telescope.php269Telescope::startRecording() src/Telescope.php231 Telescope::stopRecording() src/Telescope.php241Recording state control
IS_RECORDINGTelescope::record() src/Telescope.php281Telescope::record() src/Telescope.php292 src/Telescope.php318Recursion prevention
HAS_STOREDTelescope::record() src/Telescope.php285Telescope::record() src/Telescope.php289Defer registration flag

Sources: src/Telescope.php29-750 src/TelescopeServiceProvider.php18-180 src/ListensForStorageOpportunities.php21-177 src/Watchers/CacheWatcher.php18-156

Configuration and Extension Points

Telescope provides multiple extension mechanisms for customization:

Filtering Hooks


Filter Registration:

Lifecycle Hooks

HookTrigger PointUse Case
afterRecordingHookAfter entry queued src/Telescope.php313-315Real-time processing, alerts
afterStoringHooksAfter persistence src/Telescope.php627Analytics, notifications
tagUsingDuring recording src/Telescope.php302-304Custom tagging logic

Privacy Controls

SettingLocationPurpose
$hiddenRequestHeaderssrc/Telescope.php80-83Redact sensitive headers
$hiddenRequestParameterssrc/Telescope.php88-91Redact request body fields
$hiddenResponseParameterssrc/Telescope.php96Redact response body fields

Sources: src/Telescope.php49-76 src/Telescope.php673-707

Performance Considerations

Deferred Storage

By default, Telescope uses deferred storage (config('telescope.defer') = true) to minimize performance impact:

  1. Entries are collected in memory via Context::override(ENTRIES_QUEUE) during request processing
  2. Storage is deferred using Hyperf's defer(fn() => store()) function src/Telescope.php286-289
  3. The deferred callback executes at coroutine termination, calling Telescope::store() src/Telescope.php579-591
  4. Pending updates are dispatched to ProcessPendingUpdates queue job src/Telescope.php611-621

This ensures the main request/job/command completes quickly while persistence happens asynchronously. The defer() mechanism guarantees storage occurs even if an exception is thrown.

Sources: src/Telescope.php285-290 src/Telescope.php585-591 src/Telescope.php611-621

Recursive Recording Prevention

The IS_RECORDING flag prevents infinite loops when Telescope's own operations (logging, caching) trigger watchers:


src/Telescope.php281-283

Selective Monitoring

Watchers can be independently enabled/disabled via configuration, allowing fine-grained control over observability overhead:


Sources: src/Telescope.php281-290 src/Telescope.php585-591 src/Telescope.php611-621