adonisjs-server-stats 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +537 -0
  3. package/dist/configure.d.ts +3 -0
  4. package/dist/configure.d.ts.map +1 -0
  5. package/dist/configure.js +11 -0
  6. package/dist/src/collectors/app_collector.d.ts +17 -0
  7. package/dist/src/collectors/app_collector.d.ts.map +1 -0
  8. package/dist/src/collectors/app_collector.js +53 -0
  9. package/dist/src/collectors/collector.d.ts +67 -0
  10. package/dist/src/collectors/collector.d.ts.map +1 -0
  11. package/dist/src/collectors/collector.js +1 -0
  12. package/dist/src/collectors/db_pool_collector.d.ts +37 -0
  13. package/dist/src/collectors/db_pool_collector.d.ts.map +1 -0
  14. package/dist/src/collectors/db_pool_collector.js +49 -0
  15. package/dist/src/collectors/http_collector.d.ts +60 -0
  16. package/dist/src/collectors/http_collector.d.ts.map +1 -0
  17. package/dist/src/collectors/http_collector.js +56 -0
  18. package/dist/src/collectors/index.d.ts +14 -0
  19. package/dist/src/collectors/index.d.ts.map +1 -0
  20. package/dist/src/collectors/index.js +8 -0
  21. package/dist/src/collectors/log_collector.d.ts +48 -0
  22. package/dist/src/collectors/log_collector.d.ts.map +1 -0
  23. package/dist/src/collectors/log_collector.js +55 -0
  24. package/dist/src/collectors/process_collector.d.ts +20 -0
  25. package/dist/src/collectors/process_collector.d.ts.map +1 -0
  26. package/dist/src/collectors/process_collector.js +61 -0
  27. package/dist/src/collectors/queue_collector.d.ts +67 -0
  28. package/dist/src/collectors/queue_collector.d.ts.map +1 -0
  29. package/dist/src/collectors/queue_collector.js +57 -0
  30. package/dist/src/collectors/redis_collector.d.ts +18 -0
  31. package/dist/src/collectors/redis_collector.d.ts.map +1 -0
  32. package/dist/src/collectors/redis_collector.js +68 -0
  33. package/dist/src/collectors/system_collector.d.ts +14 -0
  34. package/dist/src/collectors/system_collector.d.ts.map +1 -0
  35. package/dist/src/collectors/system_collector.js +28 -0
  36. package/dist/src/controller/debug_controller.d.ts +10 -0
  37. package/dist/src/controller/debug_controller.d.ts.map +1 -0
  38. package/dist/src/controller/debug_controller.js +19 -0
  39. package/dist/src/controller/server_stats_controller.d.ts +8 -0
  40. package/dist/src/controller/server_stats_controller.d.ts.map +1 -0
  41. package/dist/src/controller/server_stats_controller.js +10 -0
  42. package/dist/src/debug/debug_store.d.ts +17 -0
  43. package/dist/src/debug/debug_store.d.ts.map +1 -0
  44. package/dist/src/debug/debug_store.js +26 -0
  45. package/dist/src/debug/event_collector.d.ts +20 -0
  46. package/dist/src/debug/event_collector.d.ts.map +1 -0
  47. package/dist/src/debug/event_collector.js +84 -0
  48. package/dist/src/debug/query_collector.d.ts +27 -0
  49. package/dist/src/debug/query_collector.d.ts.map +1 -0
  50. package/dist/src/debug/query_collector.js +80 -0
  51. package/dist/src/debug/ring_buffer.d.ts +21 -0
  52. package/dist/src/debug/ring_buffer.d.ts.map +1 -0
  53. package/dist/src/debug/ring_buffer.js +50 -0
  54. package/dist/src/debug/route_inspector.d.ts +18 -0
  55. package/dist/src/debug/route_inspector.d.ts.map +1 -0
  56. package/dist/src/debug/route_inspector.js +97 -0
  57. package/dist/src/debug/types.d.ts +257 -0
  58. package/dist/src/debug/types.d.ts.map +1 -0
  59. package/dist/src/debug/types.js +4 -0
  60. package/dist/src/define_config.d.ts +24 -0
  61. package/dist/src/define_config.d.ts.map +1 -0
  62. package/dist/src/define_config.js +24 -0
  63. package/dist/src/edge/client/debug-panel.css +332 -0
  64. package/dist/src/edge/client/debug-panel.js +738 -0
  65. package/dist/src/edge/client/stats-bar.css +229 -0
  66. package/dist/src/edge/client/stats-bar.js +488 -0
  67. package/dist/src/edge/plugin.d.ts +22 -0
  68. package/dist/src/edge/plugin.d.ts.map +1 -0
  69. package/dist/src/edge/plugin.js +127 -0
  70. package/dist/src/edge/views/debug-panel.edge +69 -0
  71. package/dist/src/edge/views/stats-bar.edge +47 -0
  72. package/dist/src/engine/request_metrics.d.ts +22 -0
  73. package/dist/src/engine/request_metrics.d.ts.map +1 -0
  74. package/dist/src/engine/request_metrics.js +58 -0
  75. package/dist/src/engine/stats_engine.d.ts +51 -0
  76. package/dist/src/engine/stats_engine.d.ts.map +1 -0
  77. package/dist/src/engine/stats_engine.js +71 -0
  78. package/dist/src/index.d.ts +7 -0
  79. package/dist/src/index.d.ts.map +1 -0
  80. package/dist/src/index.js +3 -0
  81. package/dist/src/log_stream/log_stream_provider.d.ts +9 -0
  82. package/dist/src/log_stream/log_stream_provider.d.ts.map +1 -0
  83. package/dist/src/log_stream/log_stream_provider.js +33 -0
  84. package/dist/src/log_stream/log_stream_service.d.ts +15 -0
  85. package/dist/src/log_stream/log_stream_service.d.ts.map +1 -0
  86. package/dist/src/log_stream/log_stream_service.js +103 -0
  87. package/dist/src/middleware/request_tracking_middleware.d.ts +7 -0
  88. package/dist/src/middleware/request_tracking_middleware.d.ts.map +1 -0
  89. package/dist/src/middleware/request_tracking_middleware.js +41 -0
  90. package/dist/src/prometheus/prometheus_collector.d.ts +9 -0
  91. package/dist/src/prometheus/prometheus_collector.d.ts.map +1 -0
  92. package/dist/src/prometheus/prometheus_collector.js +204 -0
  93. package/dist/src/provider/server_stats_provider.d.ts +13 -0
  94. package/dist/src/provider/server_stats_provider.d.ts.map +1 -0
  95. package/dist/src/provider/server_stats_provider.js +114 -0
  96. package/dist/src/stubs/config.stub +39 -0
  97. package/dist/src/stubs/main.d.ts +2 -0
  98. package/dist/src/stubs/main.d.ts.map +1 -0
  99. package/dist/src/stubs/main.js +5 -0
  100. package/dist/src/types.d.ts +354 -0
  101. package/dist/src/types.d.ts.map +1 -0
  102. package/dist/src/types.js +1 -0
  103. package/package.json +123 -0
  104. package/screenshots/debug-emails.png +0 -0
  105. package/screenshots/debug-events.png +0 -0
  106. package/screenshots/debug-logs.png +0 -0
  107. package/screenshots/debug-queries.png +0 -0
  108. package/screenshots/debug-routes.png +0 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,537 @@
1
+ # adonisjs-server-stats
2
+
3
+ A Laravel Telescope-inspired dev toolbar and real-time server monitor for **AdonisJS v6**.
4
+
5
+ Drop a single Edge tag into your layout and get a live stats bar showing CPU, memory, requests/sec, database pool, Redis, queues, and logs -- plus a full debug toolbar with SQL query inspection, event tracing, route listing, live log tailing, and custom panels.
6
+
7
+ Zero frontend dependencies. Zero build step. Just `@serverStats()` and go.
8
+
9
+ ## Screenshots
10
+
11
+ **Stats bar** -- always-on metrics strip at the bottom of every page:
12
+
13
+ ![Stats bar with live server metrics](screenshots/debug-queries.png)
14
+
15
+ **Debug toolbar** -- expandable panels for deep inspection:
16
+
17
+ | Queries | Events |
18
+ |---------|--------|
19
+ | ![Queries panel showing SQL queries with duration and model info](screenshots/debug-queries.png) | ![Events panel showing application events with payload data](screenshots/debug-events.png) |
20
+
21
+ | Routes | Logs |
22
+ |--------|------|
23
+ | ![Routes panel showing all registered routes with handlers](screenshots/debug-routes.png) | ![Logs panel with level filtering and request ID correlation](screenshots/debug-logs.png) |
24
+
25
+ | Emails (custom pane) |
26
+ |----------------------|
27
+ | ![Emails panel showing sent emails with delivery status](screenshots/debug-emails.png) |
28
+
29
+ ## Features
30
+
31
+ - **Live stats bar** -- CPU, memory, event loop lag, HTTP throughput, DB pool, Redis, queues, logs
32
+ - **Debug toolbar** -- SQL queries, events, routes, logs with search and filtering
33
+ - **Custom panes** -- add your own tabs (webhooks, emails, cache, anything) with a simple config
34
+ - **Pluggable collectors** -- use built-in collectors or write your own
35
+ - **Visibility control** -- show only to admins, specific roles, or in dev mode
36
+ - **SSE broadcasting** -- real-time updates via AdonisJS Transmit
37
+ - **Prometheus export** -- expose all metrics as Prometheus gauges
38
+ - **Self-contained** -- inline HTML/CSS/JS Edge tag, no React, no external assets
39
+ - **Graceful degradation** -- missing optional dependencies are handled automatically
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ node ace configure adonisjs-server-stats
45
+ ```
46
+
47
+ This publishes `config/server_stats.ts` and registers the providers in `adonisrc.ts`.
48
+
49
+ Or install manually:
50
+
51
+ ```bash
52
+ npm install adonisjs-server-stats
53
+ ```
54
+
55
+ ## Quick Start
56
+
57
+ ### 1. Register providers
58
+
59
+ ```ts
60
+ // adonisrc.ts
61
+ providers: [
62
+ {
63
+ file: () => import('adonisjs-server-stats/provider'),
64
+ environment: ['web'],
65
+ },
66
+ {
67
+ file: () => import('adonisjs-server-stats/log-stream/provider'),
68
+ environment: ['web'],
69
+ },
70
+ ]
71
+ ```
72
+
73
+ ### 2. Register middleware
74
+
75
+ ```ts
76
+ // start/kernel.ts
77
+ server.use([
78
+ () => import('adonisjs-server-stats/middleware'),
79
+ ])
80
+ ```
81
+
82
+ ### 3. Create config
83
+
84
+ ```ts
85
+ // config/server_stats.ts
86
+ import { defineConfig } from 'adonisjs-server-stats'
87
+ import {
88
+ processCollector,
89
+ systemCollector,
90
+ httpCollector,
91
+ dbPoolCollector,
92
+ redisCollector,
93
+ queueCollector,
94
+ logCollector,
95
+ appCollector,
96
+ } from 'adonisjs-server-stats/collectors'
97
+ import env from '#start/env'
98
+
99
+ export default defineConfig({
100
+ intervalMs: 3000,
101
+ transport: 'transmit',
102
+ channelName: 'admin/server-stats',
103
+ endpoint: '/admin/api/server-stats',
104
+ shouldShow: (ctx) => !!ctx.auth?.user?.isAdmin,
105
+ collectors: [
106
+ processCollector(),
107
+ systemCollector(),
108
+ httpCollector({ maxRecords: 10_000 }),
109
+ dbPoolCollector({ connectionName: 'postgres' }),
110
+ redisCollector(),
111
+ queueCollector({
112
+ queueName: 'default',
113
+ connection: {
114
+ host: env.get('QUEUE_REDIS_HOST'),
115
+ port: env.get('QUEUE_REDIS_PORT'),
116
+ password: env.get('QUEUE_REDIS_PASSWORD'),
117
+ },
118
+ }),
119
+ logCollector({ logPath: 'logs/adonisjs.log' }),
120
+ appCollector(),
121
+ ],
122
+ })
123
+ ```
124
+
125
+ ### 4. Add a route
126
+
127
+ ```ts
128
+ // start/routes.ts
129
+ router
130
+ .get('/admin/api/server-stats', '#controllers/admin/server_stats_controller.index')
131
+ .use(middleware.superadmin())
132
+ ```
133
+
134
+ ### 5. Create the controller
135
+
136
+ ```ts
137
+ // app/controllers/admin/server_stats_controller.ts
138
+ import app from '@adonisjs/core/services/app'
139
+ import type { HttpContext } from '@adonisjs/core/http'
140
+ import type { StatsEngine } from 'adonisjs-server-stats'
141
+
142
+ export default class ServerStatsController {
143
+ async index({ response }: HttpContext) {
144
+ const engine = await app.container.make('server_stats.engine') as StatsEngine
145
+ return response.json(engine.getLatestStats())
146
+ }
147
+ }
148
+ ```
149
+
150
+ ### 6. Render the stats bar
151
+
152
+ **Edge** (add before `</body>`):
153
+
154
+ ```edge
155
+ @serverStats()
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Config Reference
161
+
162
+ ### `ServerStatsConfig`
163
+
164
+ | Option | Type | Default | Description |
165
+ |---------------|------------------------|-----------------------------|--------------------------------------------|
166
+ | `intervalMs` | `number` | `3000` | Collection + broadcast interval (ms) |
167
+ | `transport` | `'transmit' \| 'none'` | `'transmit'` | SSE transport. `'none'` = poll-only. |
168
+ | `channelName` | `string` | `'admin/server-stats'` | Transmit channel name |
169
+ | `endpoint` | `string \| false` | `'/admin/api/server-stats'` | HTTP endpoint. `false` to disable. |
170
+ | `collectors` | `MetricCollector[]` | `[]` | Array of collector instances |
171
+ | `skipInTest` | `boolean` | `true` | Skip collection during tests |
172
+ | `onStats` | `(stats) => void` | -- | Callback after each collection tick |
173
+ | `shouldShow` | `(ctx) => boolean` | -- | Per-request visibility guard |
174
+ | `devToolbar` | `DevToolbarOptions` | -- | Dev toolbar configuration |
175
+
176
+ ### `DevToolbarOptions`
177
+
178
+ | Option | Type | Default | Description |
179
+ |------------------------|-----------------|---------|------------------------------------|
180
+ | `enabled` | `boolean` | `false` | Enable the dev toolbar |
181
+ | `maxQueries` | `number` | `500` | Max SQL queries to buffer |
182
+ | `maxEvents` | `number` | `200` | Max events to buffer |
183
+ | `slowQueryThresholdMs` | `number` | `100` | Slow query threshold (ms) |
184
+ | `panes` | `DebugPane[]` | -- | Custom debug panel tabs |
185
+
186
+ ---
187
+
188
+ ## Collectors
189
+
190
+ Each collector is a factory function that returns a `MetricCollector`. All collectors run in parallel each tick; missing peer dependencies are handled gracefully (the collector returns defaults instead of crashing).
191
+
192
+ ### Built-in Collectors
193
+
194
+ | Collector | Metrics | Options | Peer Deps |
195
+ |--------------------------|-------------------------------------------------------------|------------|---------------------|
196
+ | `processCollector()` | CPU %, event loop lag, heap/RSS memory, uptime, Node version | none | -- |
197
+ | `systemCollector()` | OS load averages, system memory, system uptime | none | -- |
198
+ | `httpCollector(opts?)` | Requests/sec, avg response time, error rate, active connections | optional | -- |
199
+ | `dbPoolCollector(opts?)` | Pool used/free/pending/max connections | optional | `@adonisjs/lucid` |
200
+ | `redisCollector()` | Status, memory, clients, keys, hit rate | none | `@adonisjs/redis` |
201
+ | `queueCollector(opts)` | Active/waiting/delayed/failed jobs, worker count | **required** | `bullmq` |
202
+ | `logCollector(opts)` | Errors/warnings/entries (5m window), entries/minute | **required** | -- |
203
+ | `appCollector()` | Online users, pending webhooks, pending emails | none | `@adonisjs/lucid` |
204
+
205
+ ### Collector Options
206
+
207
+ ```ts
208
+ httpCollector({
209
+ maxRecords: 10_000, // Circular buffer size (default: 10,000)
210
+ windowMs: 60_000, // Rolling window for rate calc (default: 60s)
211
+ })
212
+
213
+ dbPoolCollector({
214
+ connectionName: 'postgres', // Lucid connection name (default: 'postgres')
215
+ })
216
+
217
+ queueCollector({
218
+ queueName: 'default',
219
+ connection: {
220
+ host: 'localhost',
221
+ port: 6379,
222
+ password: 'secret',
223
+ },
224
+ })
225
+
226
+ logCollector({
227
+ logPath: 'logs/adonisjs.log',
228
+ })
229
+ ```
230
+
231
+ ### Custom Collectors
232
+
233
+ Implement the `MetricCollector` interface to create your own:
234
+
235
+ ```ts
236
+ import type { MetricCollector } from 'adonisjs-server-stats'
237
+
238
+ function diskCollector(): MetricCollector {
239
+ return {
240
+ name: 'disk',
241
+ async collect() {
242
+ const { availableSpace, totalSpace } = await getDiskInfo()
243
+ return {
244
+ diskAvailableGb: availableSpace / 1e9,
245
+ diskTotalGb: totalSpace / 1e9,
246
+ diskUsagePercent: ((totalSpace - availableSpace) / totalSpace) * 100,
247
+ }
248
+ },
249
+ }
250
+ }
251
+
252
+ // config/server_stats.ts
253
+ export default defineConfig({
254
+ collectors: [
255
+ processCollector(),
256
+ diskCollector(), // mix with built-in collectors
257
+ ],
258
+ })
259
+ ```
260
+
261
+ The `MetricCollector` interface:
262
+
263
+ ```ts
264
+ interface MetricCollector {
265
+ name: string
266
+ start?(): void | Promise<void>
267
+ stop?(): void | Promise<void>
268
+ collect(): Record<string, MetricValue> | Promise<Record<string, MetricValue>>
269
+ }
270
+ ```
271
+
272
+ ---
273
+
274
+ ## Visibility Control (`shouldShow`)
275
+
276
+ Control who sees the stats bar via the `shouldShow` config callback. It receives the AdonisJS `HttpContext` and returns `true` to render.
277
+
278
+ ```ts
279
+ export default defineConfig({
280
+ // Only admin users
281
+ shouldShow: (ctx) => !!ctx.auth?.user?.isAdmin,
282
+
283
+ // Only in development
284
+ shouldShow: () => process.env.NODE_ENV === 'development',
285
+
286
+ // Multiple roles
287
+ shouldShow: (ctx) =>
288
+ ctx.auth?.user?.role === 'admin' || ctx.auth?.user?.role === 'superadmin',
289
+ })
290
+ ```
291
+
292
+ When `shouldShow` is not set, the stats bar always renders.
293
+
294
+ ---
295
+
296
+ ## Edge Tag
297
+
298
+ The `@serverStats()` Edge tag renders a self-contained stats bar with inline HTML, CSS, and JS -- no external assets, no build step.
299
+
300
+ ```edge
301
+ <body>
302
+ @inertia()
303
+ @serverStats()
304
+ </body>
305
+ ```
306
+
307
+ Features:
308
+ - Polls the stats API at the configured interval
309
+ - Color-coded thresholds (green/amber/red)
310
+ - SVG sparkline charts with gradient fills
311
+ - Hover tooltips with min/max/avg stats
312
+ - Show/hide toggle (persisted via localStorage)
313
+ - Auto-hides for non-admin users (403 detection)
314
+ - Scoped CSS (`.ss-` prefix)
315
+ - Stale connection indicator (amber dot after 10s)
316
+
317
+ ---
318
+
319
+ ## Dev Toolbar
320
+
321
+ Adds a debug panel with SQL query inspection, event tracking, route table, and live logs. Only active in non-production environments.
322
+
323
+ ```ts
324
+ export default defineConfig({
325
+ devToolbar: {
326
+ enabled: true,
327
+ maxQueries: 500,
328
+ maxEvents: 200,
329
+ slowQueryThresholdMs: 100,
330
+ },
331
+ })
332
+ ```
333
+
334
+ Register the debug API routes:
335
+
336
+ ```ts
337
+ // start/routes.ts
338
+ router
339
+ .group(() => {
340
+ router.get('queries', '#controllers/admin/debug_controller.queries')
341
+ router.get('events', '#controllers/admin/debug_controller.events')
342
+ router.get('routes', '#controllers/admin/debug_controller.routes')
343
+ })
344
+ .prefix('/admin/api/debug')
345
+ .use(middleware.admin())
346
+ ```
347
+
348
+ ### Custom Debug Panes
349
+
350
+ Add custom tabs to the debug panel:
351
+
352
+ ```ts
353
+ import { defineConfig } from 'adonisjs-server-stats'
354
+ import type { DebugPane } from 'adonisjs-server-stats'
355
+
356
+ const webhooksPane: DebugPane = {
357
+ id: 'webhooks',
358
+ label: 'Webhooks',
359
+ endpoint: '/admin/api/debug/webhooks',
360
+ columns: [
361
+ { key: 'id', label: '#', width: '40px' },
362
+ { key: 'event', label: 'Event', searchable: true },
363
+ { key: 'url', label: 'URL', searchable: true },
364
+ {
365
+ key: 'status',
366
+ label: 'Status',
367
+ width: '80px',
368
+ format: 'badge',
369
+ badgeColorMap: { delivered: 'green', pending: 'amber', failed: 'red' },
370
+ },
371
+ { key: 'duration', label: 'Duration', width: '70px', format: 'duration' },
372
+ { key: 'timestamp', label: 'Time', width: '80px', format: 'timeAgo' },
373
+ ],
374
+ search: { placeholder: 'Filter webhooks by event or URL...' },
375
+ clearable: true,
376
+ }
377
+
378
+ export default defineConfig({
379
+ devToolbar: {
380
+ enabled: true,
381
+ panes: [webhooksPane],
382
+ },
383
+ })
384
+ ```
385
+
386
+ The endpoint must return JSON with the data array under a key matching the pane `id` (or `dataKey`):
387
+
388
+ ```ts
389
+ // Controller
390
+ async webhooks({ response }: HttpContext) {
391
+ const events = await WebhookEvent.query().orderBy('created_at', 'desc').limit(200)
392
+ return response.json({ webhooks: events })
393
+ }
394
+ ```
395
+
396
+ #### `DebugPane` Options
397
+
398
+ | Option | Type | Default | Description |
399
+ |-------------|---------------------|---------|----------------------------------------------|
400
+ | `id` | `string` | -- | Unique identifier (also default data key) |
401
+ | `label` | `string` | -- | Tab display name |
402
+ | `endpoint` | `string` | -- | API endpoint URL |
403
+ | `columns` | `DebugPaneColumn[]` | -- | Column definitions |
404
+ | `search` | `{ placeholder }` | -- | Enable search bar |
405
+ | `dataKey` | `string` | `id` | JSON key for data array (dot notation OK) |
406
+ | `fetchOnce` | `boolean` | `false` | Cache after first fetch |
407
+ | `clearable` | `boolean` | `false` | Show Clear button |
408
+
409
+ #### `DebugPaneColumn` Options
410
+
411
+ | Option | Type | Default | Description |
412
+ |-----------------|--------------------------|----------|------------------------------------------|
413
+ | `key` | `string` | -- | JSON field name |
414
+ | `label` | `string` | -- | Column header text |
415
+ | `width` | `string` | auto | CSS width (e.g. `'60px'`) |
416
+ | `format` | `DebugPaneFormatType` | `'text'` | Cell format (see table below) |
417
+ | `searchable` | `boolean` | `false` | Include in search filtering |
418
+ | `filterable` | `boolean` | `false` | Click to set as search filter |
419
+ | `badgeColorMap` | `Record<string, string>` | -- | Value-to-color map for `badge` format |
420
+
421
+ #### Format Types
422
+
423
+ | Format | Renders As | Expected Input |
424
+ |------------|----------------------------------------|-------------------------|
425
+ | `text` | Escaped plain text | any |
426
+ | `time` | `HH:MM:SS.mmm` | Unix timestamp (ms) |
427
+ | `timeAgo` | `3s ago`, `2m ago` | Unix timestamp (ms) |
428
+ | `duration` | `X.XXms` with color coding | number (ms) |
429
+ | `method` | HTTP method pill badge | `'GET'`, `'POST'`, etc. |
430
+ | `json` | Compact preview, click to expand | object or array |
431
+ | `badge` | Colored pill via `badgeColorMap` | string |
432
+
433
+ Badge colors: `green`, `amber`, `red`, `blue`, `purple`, `muted`
434
+
435
+ ---
436
+
437
+ ## Prometheus Integration
438
+
439
+ Export all metrics as Prometheus gauges. Requires `@julr/adonisjs-prometheus`.
440
+
441
+ ```ts
442
+ // config/prometheus.ts
443
+ import { defineConfig } from '@julr/adonisjs-prometheus'
444
+ import { httpCollector } from '@julr/adonisjs-prometheus/collectors/http_collector'
445
+ import { serverStatsCollector } from 'adonisjs-server-stats/prometheus'
446
+
447
+ export default defineConfig({
448
+ endpoint: '/metrics',
449
+ collectors: [httpCollector(), serverStatsCollector()],
450
+ })
451
+ ```
452
+
453
+ Gauges are updated automatically on each collection tick.
454
+
455
+ ---
456
+
457
+ ## Log Stream
458
+
459
+ The log stream module watches a JSON log file and broadcasts new entries via Transmit (SSE).
460
+
461
+ **Two purposes:**
462
+ 1. Provides error/warning counts to the stats bar via `logCollector()`
463
+ 2. Broadcasts individual log entries to a Transmit channel via `LogStreamProvider`
464
+
465
+ Standalone usage:
466
+
467
+ ```ts
468
+ import { LogStreamService } from 'adonisjs-server-stats/log-stream'
469
+
470
+ const service = new LogStreamService('logs/app.log', (entry) => {
471
+ console.log('New log entry:', entry)
472
+ })
473
+
474
+ await service.start()
475
+ // later...
476
+ service.stop()
477
+ ```
478
+
479
+ ---
480
+
481
+ ## TypeScript
482
+
483
+ All types are exported for consumer use:
484
+
485
+ ```ts
486
+ // Core types
487
+ import type {
488
+ ServerStats,
489
+ ServerStatsConfig,
490
+ MetricCollector,
491
+ MetricValue,
492
+ LogStats,
493
+ DevToolbarOptions,
494
+ } from 'adonisjs-server-stats'
495
+
496
+ // Debug types
497
+ import type {
498
+ DebugPane,
499
+ DebugPaneColumn,
500
+ DebugPaneFormatType,
501
+ DebugPaneSearch,
502
+ BadgeColor,
503
+ QueryRecord,
504
+ EventRecord,
505
+ RouteRecord,
506
+ } from 'adonisjs-server-stats'
507
+
508
+ // Collector option types
509
+ import type {
510
+ HttpCollectorOptions,
511
+ DbPoolCollectorOptions,
512
+ QueueCollectorOptions,
513
+ QueueRedisConnection,
514
+ LogCollectorOptions,
515
+ } from 'adonisjs-server-stats/collectors'
516
+
517
+ ```
518
+
519
+ ---
520
+
521
+ ## Peer Dependencies
522
+
523
+ All integrations use lazy `import()` -- missing peer deps won't crash the app. The corresponding collector simply returns defaults.
524
+
525
+ | Dependency | Required By |
526
+ |-----------------------------|-----------------------------------|
527
+ | `@adonisjs/core` | Everything (required) |
528
+ | `@adonisjs/lucid` | `dbPoolCollector`, `appCollector` |
529
+ | `@adonisjs/redis` | `redisCollector` |
530
+ | `@adonisjs/transmit` | Provider (SSE broadcast) |
531
+ | `@julr/adonisjs-prometheus` | `serverStatsCollector` |
532
+ | `bullmq` | `queueCollector` |
533
+ | `edge.js` | Edge tag |
534
+
535
+ ## License
536
+
537
+ MIT
@@ -0,0 +1,3 @@
1
+ import type Configure from '@adonisjs/core/commands/configure';
2
+ export declare function configure(command: Configure): Promise<void>;
3
+ //# sourceMappingURL=configure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,mCAAmC,CAAA;AAI9D,wBAAsB,SAAS,CAAC,OAAO,EAAE,SAAS,iBAWjD"}
@@ -0,0 +1,11 @@
1
+ import { stubsRoot } from './src/stubs/main.js';
2
+ export async function configure(command) {
3
+ const codemods = await command.createCodemods();
4
+ // Publish config file
5
+ await codemods.makeUsingStub(stubsRoot(), 'config.stub', {});
6
+ // Register provider in adonisrc.ts
7
+ await codemods.updateRcFile((rcFile) => {
8
+ rcFile.addProvider('adonisjs-server-stats/provider', ['web']);
9
+ rcFile.addProvider('adonisjs-server-stats/log-stream/provider', ['web']);
10
+ });
11
+ }
@@ -0,0 +1,17 @@
1
+ import type { MetricCollector } from './collector.js';
2
+ /**
3
+ * Queries application-specific tables for user sessions,
4
+ * pending webhooks, and pending emails.
5
+ *
6
+ * Expects `sessions`, `webhook_events`, and `scheduled_emails` tables
7
+ * to exist. Missing tables are silently ignored (returns 0).
8
+ *
9
+ * **Metrics produced:**
10
+ * - `onlineUsers` -- active session count
11
+ * - `pendingWebhooks` -- webhook events awaiting delivery
12
+ * - `pendingEmails` -- scheduled emails awaiting send
13
+ *
14
+ * **Peer dependencies:** `@adonisjs/lucid`
15
+ */
16
+ export declare function appCollector(): MetricCollector;
17
+ //# sourceMappingURL=app_collector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app_collector.d.ts","sourceRoot":"","sources":["../../../src/collectors/app_collector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAErD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,IAAI,eAAe,CAuC9C"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Queries application-specific tables for user sessions,
3
+ * pending webhooks, and pending emails.
4
+ *
5
+ * Expects `sessions`, `webhook_events`, and `scheduled_emails` tables
6
+ * to exist. Missing tables are silently ignored (returns 0).
7
+ *
8
+ * **Metrics produced:**
9
+ * - `onlineUsers` -- active session count
10
+ * - `pendingWebhooks` -- webhook events awaiting delivery
11
+ * - `pendingEmails` -- scheduled emails awaiting send
12
+ *
13
+ * **Peer dependencies:** `@adonisjs/lucid`
14
+ */
15
+ export function appCollector() {
16
+ return {
17
+ name: 'app',
18
+ async collect() {
19
+ try {
20
+ const { default: db } = await import('@adonisjs/lucid/services/db');
21
+ const [sessions, webhooks, emails] = await Promise.all([
22
+ db
23
+ .from('sessions')
24
+ .count('* as total')
25
+ .first()
26
+ .then((r) => Number(r?.total ?? 0)),
27
+ db
28
+ .from('webhook_events')
29
+ .where('status', 'pending')
30
+ .count('* as total')
31
+ .first()
32
+ .then((r) => Number(r?.total ?? 0))
33
+ .catch(() => 0),
34
+ db
35
+ .from('scheduled_emails')
36
+ .where('status', 'pending')
37
+ .count('* as total')
38
+ .first()
39
+ .then((r) => Number(r?.total ?? 0))
40
+ .catch(() => 0),
41
+ ]);
42
+ return {
43
+ onlineUsers: sessions,
44
+ pendingWebhooks: webhooks,
45
+ pendingEmails: emails,
46
+ };
47
+ }
48
+ catch {
49
+ return { onlineUsers: 0, pendingWebhooks: 0, pendingEmails: 0 };
50
+ }
51
+ },
52
+ };
53
+ }