Getting Started
Install @adonis-agora/telescope, configure it into your AdonisJS app, read entries back from the headless API, then layer on persistent storage, per-technology watchers, and the dashboard.
This walks you from a bare AdonisJS app to a fully instrumented one: requests, queries, exceptions and diagnostics events captured, queryable, and visible in a dashboard.
Prerequisites
- AdonisJS 7 (
@adonisjs/core^7.3.0). - Node 20.6+ (the diagnostics integration uses
node:diagnostics_channel). - Optional but recommended:
@adonis-agora/contextfor trace correlation and@adonis-agora/diagnosticsso library events flow into Telescope.
Step 1 — Install the core
Add the package
npm i @adonis-agora/telescope
node ace configure @adonis-agora/telescopeWhat configure does
node ace configure @adonis-agora/telescope runs codemods that:
- register
@adonis-agora/telescope/telescope_providerinadonisrc.ts; - register
TelescopeMiddlewareon the server middleware stack (so it wraps the whole HTTP pipeline and observes the final response status); - publish
config/telescope.ts.
That is everything the core needs. With both default watchers on, every inbound
request is recorded as a request entry, and every agora:<lib>:<event> diagnostics
publish is recorded as a diagnostic entry.
Step 2 — Configure (optional)
The published config/telescope.ts is all-defaults — you only edit it to change
behaviour:
import { defineConfig, storage } from '@adonis-agora/telescope'
export default defineConfig({
enabled: true, // master switch
store: 'memory', // which driver in `stores` is active
stores: {
memory: storage.memory({ limit: 1000 }), // bounded in-process ring buffer
},
watchers: ['request', 'diagnostics'], // omit one to disable it
})| Key | Default | What it does |
|---|---|---|
enabled | true | Master switch. false → records nothing, watchers stay dormant. |
store | 'memory' | Which named driver in stores is active (or a TelescopeStore instance). |
stores | {} | Named drivers built with the storage factory (memory / lucid). |
watchers | ['request', 'diagnostics'] | Which built-in watchers run. Omit a name to disable it. |
extensions | [] | Extensions contributing entry types, dashboards, and data providers. |
See the configuration reference for every key.
Step 3 — Hit the headless API
Telescope is headless first: the dashboard is optional, and everything it shows is
served from TelescopeService. Resolve it from the container and read entries back.
import { inject } from '@adonisjs/core'
import type { HttpContext } from '@adonisjs/core/http'
import { TelescopeService } from '@adonis-agora/telescope'
@inject()
export default class InspectorController {
constructor(private telescope: TelescopeService) {}
async index({ response }: HttpContext) {
return response.json(await this.telescope.list({ size: 50 }))
}
}@inject() on the class asks the container to construct the controller's dependencies from
their constructor types, so TelescopeService arrives already resolved. A controller has no
app of its own, so reaching for this.app.container.make(...) inside one will not compile.
The full surface:
| Method | Returns |
|---|---|
list(query?) | Entries matching the EntryQuery, newest-first. |
find(id) | One Entry, or null. |
byTrace(traceId) | Every entry recorded under a trace, newest-first. |
count() | Total stored entries. |
topFamilies(limit?, type?) | Busiest groups by familyHash (e.g. busiest lib:event pairs). |
topTags(limit?, prefix?) | Most common tags, optionally by prefix. |
The EntryQuery composes filters with AND: { type, tag, familyHash, traceId, before, after, search, page, size }
— page is 1-based and size is the page size, the same pagination pair every @adonis-agora/* library uses.
await telescope.list({ type: 'request', size: 20 })
await telescope.list({ tag: 'lib:billing', search: 'invoice' })
await telescope.byTrace('abc123')
await telescope.topFamilies(10, 'diagnostic')Step 4 — Pick your storage
The default memory driver is a bounded ring buffer — fast, dependency-free, and
lost on restart. Great for dev and tests. For a store that survives restarts and
is queryable straight from your database, switch to the built-in lucid driver. It
needs @adonisjs/lucid (an optional peer, imported only when selected):
npm i @adonisjs/lucid
node ace migration:run # runs the migration configure already publishednode ace configure @adonis-agora/telescope ships the create_telescope_entries_table
migration alongside the config file. Then point the config at the lucid driver:
import { defineConfig, storage } from '@adonis-agora/telescope'
export default defineConfig({
store: 'lucid',
stores: {
lucid: storage.lucid(), // or storage.lucid({ connection: 'pg' })
},
})The lucid driver implements the same TelescopeStore contract and works on every
Lucid dialect (sqlite / Postgres / MySQL). See Storage drivers.
Step 5 — Add per-technology watchers
The core records requests, exceptions and diagnostics. To also record every Lucid
SQL query (and optionally mail / cache events), enable the watchers feature — it ships
inside @adonis-agora/telescope, so re-run configure and pick Watchers:
node ace configure @adonis-agora/telescope # pick "Watchers"By default only the verified Lucid query watcher runs. Enable mail and cache in
config/telescope_watchers.ts:
import { defineConfig } from '@adonis-agora/telescope/watchers'
export default defineConfig({
watchers: ['query', 'mail', 'cache'],
})Lucid only emits db:query when the connection's debug flag is on (or a db:query
listener exists at report time). Subscribing the watcher is enough to make Lucid
report; setting debug: true in your DB config guarantees it.
See Watchers for each watcher's recorded shape and caveats.
Step 6 — Mount the dashboard
The core UI feature (the @adonis-agora/telescope/ui subpath) serves the JSON API and
live-stream under <path>/api/* — it does not render a page. The browsable dashboard is a
separate package, @adonis-agora/telescope-ui, a pre-built React SPA (no build step in your
app) served under that same prefix and auth guard.
Enable both:
node ace configure @adonis-agora/telescope # pick "UI" (JSON API + SSE)
npm i @adonis-agora/telescope-ui # the React dashboard SPAThen register @adonis-agora/telescope-ui/telescope_ui_dashboard_provider after the core
ui_provider in adonisrc.ts. With both providers registered, the dashboard mounts at
/telescope and is reachable automatically outside production. In production it is denied
unless you set a credential or supply an authorize hook. See Dashboard auth.
Next steps
- Capture & correlation — the mental model: watchers, the
Entry, the diagnostics spine, trace correlation. - Storage — the store contract, retention, and pruning.
- Dashboard tour — what the console shows and how to mount it.
- Alerts — page on new exception families.
- AI exception diagnosis — structured root-cause analysis.
- Configuration reference — every key, every package.
Telescope
Laravel Telescope-style observability for AdonisJS — a generic capture spine records every HTTP request, every Lucid query, and every Agora diagnostics event as a queryable entry, browsable from a self-contained dashboard, with alerts and AI exception diagnosis on top.
Capture & correlation
The mental model behind Telescope — what a watcher is, the uniform Entry shape every recording produces, the generic diagnostics spine that records all library events, exception auto-capture, and how trace correlation works without coupling to @adonis-agora/context.