adonisjs-server-stats 1.10.0 → 1.10.3
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.
- package/README.md +23 -14
- package/dist/core/config-utils.d.ts +8 -0
- package/dist/core/constants.d.ts +4 -0
- package/dist/core/dashboard-data-controller.d.ts +16 -0
- package/dist/core/dashboard-data-helpers.d.ts +12 -0
- package/dist/core/debug-data-controller.d.ts +4 -0
- package/dist/core/define-config-helpers.d.ts +25 -0
- package/dist/core/feature-detect-helpers.d.ts +36 -0
- package/dist/core/formatters-helpers.d.ts +23 -0
- package/dist/core/index.js +594 -509
- package/dist/core/log-utils-helpers.d.ts +13 -0
- package/dist/core/metrics.d.ts +3 -28
- package/dist/core/pagination.d.ts +0 -9
- package/dist/core/server-stats-controller.d.ts +6 -0
- package/dist/core/transmit-helpers.d.ts +7 -0
- package/dist/core/types-dashboard.d.ts +178 -0
- package/dist/core/types-diagnostics.d.ts +85 -0
- package/dist/core/types.d.ts +10 -442
- package/dist/react/{CacheSection-UCMptWyn.js → CacheSection-baMZotSn.js} +2 -2
- package/dist/react/CacheTab-2cw_rMzj.js +117 -0
- package/dist/react/{ConfigSection-DfFd-WRq.js → ConfigSection-DGgqjAal.js} +1 -1
- package/dist/react/{ConfigTab-Bdg8YMer.js → ConfigTab-H3OnYqmK.js} +1 -1
- package/dist/react/CustomPaneTab-B6r7ha0u.js +98 -0
- package/dist/react/{EmailsSection-CM7stSyh.js → EmailsSection-C-UZISG-.js} +2 -2
- package/dist/react/EmailsTab-DbK4Eobn.js +139 -0
- package/dist/react/{EventsSection-ByQ-9blq.js → EventsSection-C7RQW_LY.js} +2 -2
- package/dist/react/EventsTab-CfVr7AiM.js +57 -0
- package/dist/react/{FilterBar-DQRXpWrb.js → FilterBar-CQ7bD669.js} +15 -15
- package/dist/react/{JobsSection-DF3qEv9O.js → JobsSection-CQHNK_Ls.js} +2 -2
- package/dist/react/{JobsTab-BbrBWIOb.js → JobsTab-znzf6jzk.js} +54 -42
- package/dist/react/{LogsSection-DcFTZY7b.js → LogsSection-Dmm3rE2B.js} +9 -3
- package/dist/react/LogsTab-D8unMV5P.js +108 -0
- package/dist/react/{OverviewSection-C4T1ur51.js → OverviewSection-ABP9ueBo.js} +1 -1
- package/dist/react/{QueriesSection-PswteoF9.js → QueriesSection-CnmSkznA.js} +2 -2
- package/dist/react/{QueriesTab-osLUWd4L.js → QueriesTab-BQzcxEiW.js} +37 -40
- package/dist/react/{RelatedLogs-DFDOyUMr.js → RelatedLogs-3A8RuGKH.js} +15 -3
- package/dist/react/{RequestsSection-Nag30rEA.js → RequestsSection-kW79_M7k.js} +3 -3
- package/dist/react/{RoutesSection-BUSkM6PY.js → RoutesSection-BRhxrtjZ.js} +2 -2
- package/dist/react/RoutesTab-CpYH5lUw.js +68 -0
- package/dist/react/{TimelineTab-Covg5weo.js → TimelineTab-DjLR35Ce.js} +47 -53
- package/dist/react/index-CsImORX6.js +1121 -0
- package/dist/react/index.js +1 -1
- package/dist/react/react/components/{Dashboard/shared → shared}/FilterBar.d.ts +4 -3
- package/dist/react/react/hooks/useDashboardData.d.ts +4 -8
- package/dist/react/style.css +1 -1
- package/dist/src/collectors/app_collector.d.ts +0 -8
- package/dist/src/collectors/app_collector.js +45 -52
- package/dist/src/collectors/auto_detect.d.ts +0 -23
- package/dist/src/collectors/auto_detect.js +33 -55
- package/dist/src/collectors/db_pool_collector.d.ts +14 -16
- package/dist/src/collectors/db_pool_collector.js +72 -57
- package/dist/src/collectors/log_collector.d.ts +0 -47
- package/dist/src/collectors/log_collector.js +36 -65
- package/dist/src/collectors/queue_collector.d.ts +0 -20
- package/dist/src/collectors/queue_collector.js +60 -76
- package/dist/src/collectors/redis_collector.d.ts +10 -10
- package/dist/src/collectors/redis_collector.js +69 -66
- package/dist/src/config/deprecation_migration.d.ts +7 -0
- package/dist/src/config/deprecation_migration.js +201 -0
- package/dist/src/controller/debug_controller.d.ts +1 -1
- package/dist/src/controller/debug_controller.js +87 -81
- package/dist/src/dashboard/cache_handlers.d.ts +14 -0
- package/dist/src/dashboard/cache_handlers.js +52 -0
- package/dist/src/dashboard/chart_aggregator.d.ts +0 -7
- package/dist/src/dashboard/chart_aggregator.js +68 -50
- package/dist/src/dashboard/coalesce_cache.d.ts +25 -0
- package/dist/src/dashboard/coalesce_cache.js +47 -0
- package/dist/src/dashboard/dashboard_controller.d.ts +11 -37
- package/dist/src/dashboard/dashboard_controller.js +51 -544
- package/dist/src/dashboard/dashboard_page_assets.d.ts +17 -0
- package/dist/src/dashboard/dashboard_page_assets.js +51 -0
- package/dist/src/dashboard/dashboard_store.d.ts +19 -218
- package/dist/src/dashboard/dashboard_store.js +115 -1116
- package/dist/src/dashboard/dashboard_types.d.ts +83 -0
- package/dist/src/dashboard/dashboard_types.js +4 -0
- package/dist/src/dashboard/detail_queries.d.ts +19 -0
- package/dist/src/dashboard/detail_queries.js +98 -0
- package/dist/src/dashboard/email_event_builder.d.ts +8 -0
- package/dist/src/dashboard/email_event_builder.js +65 -0
- package/dist/src/dashboard/explain_query.d.ts +8 -0
- package/dist/src/dashboard/explain_query.js +22 -0
- package/dist/src/dashboard/filter_handlers.d.ts +23 -0
- package/dist/src/dashboard/filter_handlers.js +56 -0
- package/dist/src/dashboard/filtered_queries.d.ts +15 -0
- package/dist/src/dashboard/filtered_queries.js +155 -0
- package/dist/src/dashboard/flush_manager.d.ts +25 -0
- package/dist/src/dashboard/flush_manager.js +107 -0
- package/dist/src/dashboard/format_helpers.d.ts +126 -0
- package/dist/src/dashboard/format_helpers.js +140 -0
- package/dist/src/dashboard/inspector_manager.d.ts +36 -0
- package/dist/src/dashboard/inspector_manager.js +102 -0
- package/dist/src/dashboard/integrations/config_inspector.js +11 -13
- package/dist/src/dashboard/integrations/queue_inspector.d.ts +3 -3
- package/dist/src/dashboard/integrations/queue_inspector.js +13 -10
- package/dist/src/dashboard/jobs_handlers.d.ts +14 -0
- package/dist/src/dashboard/jobs_handlers.js +61 -0
- package/dist/src/dashboard/knex_factory.d.ts +18 -0
- package/dist/src/dashboard/knex_factory.js +91 -0
- package/dist/src/dashboard/migrator.js +30 -159
- package/dist/src/dashboard/migrator_tables.d.ts +19 -0
- package/dist/src/dashboard/migrator_tables.js +153 -0
- package/dist/src/dashboard/overview_queries.d.ts +66 -0
- package/dist/src/dashboard/overview_queries.js +155 -0
- package/dist/src/dashboard/overview_query_runners.d.ts +25 -0
- package/dist/src/dashboard/overview_query_runners.js +84 -0
- package/dist/src/dashboard/overview_store_queries.d.ts +40 -0
- package/dist/src/dashboard/overview_store_queries.js +69 -0
- package/dist/src/dashboard/paginate_helper.d.ts +12 -0
- package/dist/src/dashboard/paginate_helper.js +33 -0
- package/dist/src/dashboard/query_explain_handler.d.ts +10 -0
- package/dist/src/dashboard/query_explain_handler.js +80 -0
- package/dist/src/dashboard/read_queries.d.ts +32 -0
- package/dist/src/dashboard/read_queries.js +107 -0
- package/dist/src/dashboard/saved_filter_queries.d.ts +10 -0
- package/dist/src/dashboard/saved_filter_queries.js +24 -0
- package/dist/src/dashboard/storage_stats.d.ts +41 -0
- package/dist/src/dashboard/storage_stats.js +81 -0
- package/dist/src/dashboard/write_queue.d.ts +106 -0
- package/dist/src/dashboard/write_queue.js +225 -0
- package/dist/src/data/data_access.d.ts +2 -39
- package/dist/src/data/data_access.js +17 -193
- package/dist/src/data/data_access_helpers.d.ts +130 -0
- package/dist/src/data/data_access_helpers.js +212 -0
- package/dist/src/debug/debug_store.js +37 -32
- package/dist/src/debug/email_collector.d.ts +1 -10
- package/dist/src/debug/email_collector.js +78 -81
- package/dist/src/debug/event_collector.d.ts +0 -9
- package/dist/src/debug/event_collector.js +79 -62
- package/dist/src/debug/query_collector.js +23 -19
- package/dist/src/debug/route_inspector.d.ts +1 -5
- package/dist/src/debug/route_inspector.js +50 -51
- package/dist/src/debug/trace_collector.d.ts +9 -1
- package/dist/src/debug/trace_collector.js +21 -15
- package/dist/src/debug/types.d.ts +1 -1
- package/dist/src/define_config.d.ts +0 -65
- package/dist/src/define_config.js +93 -333
- package/dist/src/edge/client/dashboard.js +2 -2
- package/dist/src/edge/client/debug-panel-deferred.js +1 -1
- package/dist/src/edge/client/stats-bar.js +1 -1
- package/dist/src/edge/client-vue/dashboard.js +5 -5
- package/dist/src/edge/client-vue/debug-panel-deferred.js +3 -3
- package/dist/src/edge/client-vue/stats-bar.js +3 -3
- package/dist/src/edge/plugin.d.ts +0 -16
- package/dist/src/edge/plugin.js +57 -64
- package/dist/src/engine/request_metrics.d.ts +1 -0
- package/dist/src/engine/request_metrics.js +32 -42
- package/dist/src/middleware/request_tracking_middleware.d.ts +2 -8
- package/dist/src/middleware/request_tracking_middleware.js +65 -93
- package/dist/src/provider/auth_middleware_detector.d.ts +16 -0
- package/dist/src/provider/auth_middleware_detector.js +97 -0
- package/dist/src/provider/boot_helpers.d.ts +20 -0
- package/dist/src/provider/boot_helpers.js +91 -0
- package/dist/src/provider/boot_initializer.d.ts +28 -0
- package/dist/src/provider/boot_initializer.js +35 -0
- package/dist/src/provider/dashboard_init.d.ts +30 -0
- package/dist/src/provider/dashboard_init.js +138 -0
- package/dist/src/provider/dashboard_setup.d.ts +25 -0
- package/dist/src/provider/dashboard_setup.js +78 -0
- package/dist/src/provider/diagnostics.d.ts +134 -0
- package/dist/src/provider/diagnostics.js +127 -0
- package/dist/src/provider/email_bridge.d.ts +43 -0
- package/dist/src/provider/email_bridge.js +80 -0
- package/dist/src/provider/email_helpers.d.ts +13 -0
- package/dist/src/provider/email_helpers.js +68 -0
- package/dist/src/provider/pino_hook.d.ts +17 -0
- package/dist/src/provider/pino_hook.js +35 -0
- package/dist/src/provider/provider_helpers_extra.d.ts +47 -0
- package/dist/src/provider/provider_helpers_extra.js +177 -0
- package/dist/src/provider/server_stats_provider.d.ts +39 -85
- package/dist/src/provider/server_stats_provider.js +132 -951
- package/dist/src/provider/shutdown_helpers.d.ts +43 -0
- package/dist/src/provider/shutdown_helpers.js +70 -0
- package/dist/src/provider/toolbar_setup.d.ts +57 -0
- package/dist/src/provider/toolbar_setup.js +141 -0
- package/dist/src/routes/dashboard_routes.d.ts +14 -0
- package/dist/src/routes/dashboard_routes.js +197 -0
- package/dist/src/routes/debug_routes.d.ts +14 -0
- package/dist/src/routes/debug_routes.js +101 -0
- package/dist/src/routes/register_routes.d.ts +0 -78
- package/dist/src/routes/register_routes.js +22 -352
- package/dist/src/routes/stats_routes.d.ts +5 -0
- package/dist/src/routes/stats_routes.js +14 -0
- package/dist/src/styles/components.css +96 -0
- package/dist/src/styles/dashboard.css +8 -90
- package/dist/src/styles/debug-panel.css +1 -31
- package/dist/src/types.d.ts +305 -14
- package/dist/vue/{CacheSection-oFAJL3mo.js → CacheSection-ITqvpfH5.js} +1 -1
- package/dist/vue/{ConfigSection-BhfJ4KqL.js → ConfigSection-DTn3GslE.js} +1 -1
- package/dist/vue/{EmailsSection-BcNyhyHs.js → EmailsSection-DtLJ4XoS.js} +1 -1
- package/dist/vue/{EventsSection-r60Q5Lmu.js → EventsSection-BOYYz0Ty.js} +1 -1
- package/dist/vue/{JobsSection-BHL-hkQw.js → JobsSection-BazTxcJL.js} +1 -1
- package/dist/vue/{LogsSection-DRMGzJmg.js → LogsSection-D55PjTKX.js} +9 -3
- package/dist/vue/{LogsTab-Bg3o0Mm6.js → LogsTab-47zEK7jL.js} +4 -1
- package/dist/vue/{OverviewSection-CXh6Ja1B.js → OverviewSection-1uBKo-Tu.js} +1 -1
- package/dist/vue/{QueriesSection-IodIsCJ-.js → QueriesSection-rpoZ4ogd.js} +1 -1
- package/dist/vue/{RequestsSection-BPuMdmMc.js → RequestsSection-x7LvT0MC.js} +1 -1
- package/dist/vue/{RoutesSection-NKo3Rbq3.js → RoutesSection-CCD0zZqQ.js} +1 -1
- package/dist/vue/composables/useDashboardData.d.ts +12 -23
- package/dist/vue/index-C8MxnS7Q.js +1232 -0
- package/dist/vue/index.js +1 -1
- package/dist/vue/style.css +1 -1
- package/package.json +1 -1
- package/dist/react/CacheTab-CA8LB1J5.js +0 -123
- package/dist/react/CustomPaneTab-Bxtv_8Rw.js +0 -104
- package/dist/react/EmailsTab-BDhEiomM.js +0 -153
- package/dist/react/EventsTab-CMfY98Rl.js +0 -63
- package/dist/react/LogsTab-CicucmVk.js +0 -103
- package/dist/react/RoutesTab-DgVzd2PZ.js +0 -74
- package/dist/react/index-Cflz9Ebj.js +0 -1069
- package/dist/vue/index-Dtgysd26.js +0 -1229
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import { appImport } from '../utils/app_import.js';
|
|
2
2
|
import { log, dim, green, bold } from '../utils/logger.js';
|
|
3
|
-
/**
|
|
4
|
-
* Probe whether a package is importable at runtime.
|
|
5
|
-
*
|
|
6
|
-
* Uses {@link appImport} which resolves from `process.cwd()` first,
|
|
7
|
-
* handling the common case where adonisjs-server-stats is symlinked
|
|
8
|
-
* (e.g. `file:../../adonisjs-server-stats` in package.json). Without
|
|
9
|
-
* this, `import(pkg)` resolves from the symlink *target* directory,
|
|
10
|
-
* which may have devDependency stubs instead of the real packages.
|
|
11
|
-
*/
|
|
12
3
|
async function isInstalled(pkg) {
|
|
13
4
|
try {
|
|
14
5
|
await appImport(pkg);
|
|
@@ -18,29 +9,16 @@ async function isInstalled(pkg) {
|
|
|
18
9
|
return false;
|
|
19
10
|
}
|
|
20
11
|
}
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
* - `dbPoolCollector` + `appCollector` -- when `@adonisjs/lucid` is installed
|
|
32
|
-
* - `redisCollector` -- when `@adonisjs/redis` is installed
|
|
33
|
-
* - `queueCollector` -- when `bullmq` is installed
|
|
34
|
-
*
|
|
35
|
-
* This function is meant to run at startup, so the async overhead of
|
|
36
|
-
* probing packages is acceptable.
|
|
37
|
-
*
|
|
38
|
-
* @returns Resolved collectors and detection metadata for the boot summary.
|
|
39
|
-
*/
|
|
40
|
-
export async function autoDetectCollectors() {
|
|
41
|
-
const collectors = [];
|
|
42
|
-
const entries = [];
|
|
43
|
-
// ── Always-on collectors (no external deps) ──────────────────────
|
|
12
|
+
/** Push an optional collector entry with auto-generated reason. */
|
|
13
|
+
function pushOptionalEntry(entries, opts) {
|
|
14
|
+
entries.push({
|
|
15
|
+
name: opts.name,
|
|
16
|
+
description: opts.description,
|
|
17
|
+
enabled: opts.enabled,
|
|
18
|
+
reason: opts.enabled ? `found ${opts.pkg}` : `install ${opts.pkg} to enable`,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async function registerCoreCollectors(collectors, entries) {
|
|
44
22
|
const { processCollector } = await import('./process_collector.js');
|
|
45
23
|
const { systemCollector } = await import('./system_collector.js');
|
|
46
24
|
const { httpCollector } = await import('./http_collector.js');
|
|
@@ -53,7 +31,8 @@ export async function autoDetectCollectors() {
|
|
|
53
31
|
entries.push({ name: 'http', description: 'req/s, latency, errors', enabled: true });
|
|
54
32
|
collectors.push(logCollector());
|
|
55
33
|
entries.push({ name: 'log', description: 'error & warning counts', enabled: true });
|
|
56
|
-
|
|
34
|
+
}
|
|
35
|
+
async function registerOptionalCollectors(collectors, entries) {
|
|
57
36
|
const hasLucid = await isInstalled('@adonisjs/lucid');
|
|
58
37
|
if (hasLucid) {
|
|
59
38
|
const { dbPoolCollector } = await import('./db_pool_collector.js');
|
|
@@ -61,60 +40,59 @@ export async function autoDetectCollectors() {
|
|
|
61
40
|
collectors.push(dbPoolCollector());
|
|
62
41
|
collectors.push(appCollector());
|
|
63
42
|
}
|
|
64
|
-
entries
|
|
43
|
+
pushOptionalEntry(entries, {
|
|
65
44
|
name: 'db-pool',
|
|
66
45
|
description: 'connection pool stats',
|
|
67
46
|
enabled: hasLucid,
|
|
68
|
-
|
|
47
|
+
pkg: '@adonisjs/lucid',
|
|
69
48
|
});
|
|
70
|
-
entries
|
|
49
|
+
pushOptionalEntry(entries, {
|
|
71
50
|
name: 'app',
|
|
72
51
|
description: 'app-level DB metrics',
|
|
73
52
|
enabled: hasLucid,
|
|
74
|
-
|
|
53
|
+
pkg: '@adonisjs/lucid',
|
|
75
54
|
});
|
|
76
|
-
// ── Conditional: @adonisjs/redis ─────────────────────────────────
|
|
77
55
|
const hasRedis = await isInstalled('@adonisjs/redis');
|
|
78
56
|
if (hasRedis) {
|
|
79
57
|
const { redisCollector } = await import('./redis_collector.js');
|
|
80
58
|
collectors.push(redisCollector());
|
|
81
59
|
}
|
|
82
|
-
entries
|
|
60
|
+
pushOptionalEntry(entries, {
|
|
83
61
|
name: 'redis',
|
|
84
62
|
description: 'connections, commands, memory',
|
|
85
63
|
enabled: hasRedis,
|
|
86
|
-
|
|
64
|
+
pkg: '@adonisjs/redis',
|
|
87
65
|
});
|
|
88
|
-
// ── Conditional: bullmq ──────────────────────────────────────────
|
|
89
66
|
const hasBullMQ = await isInstalled('bullmq');
|
|
90
67
|
if (hasBullMQ) {
|
|
91
68
|
const { queueCollector } = await import('./queue_collector.js');
|
|
92
|
-
collectors.push(queueCollector({
|
|
93
|
-
queueName: 'default',
|
|
94
|
-
connection: { host: '127.0.0.1', port: 6379 },
|
|
95
|
-
}));
|
|
69
|
+
collectors.push(queueCollector({ queueName: 'default', connection: { host: '127.0.0.1', port: 6379 } }));
|
|
96
70
|
}
|
|
97
|
-
entries
|
|
71
|
+
pushOptionalEntry(entries, {
|
|
98
72
|
name: 'queue',
|
|
99
73
|
description: 'jobs, wait time, throughput',
|
|
100
74
|
enabled: hasBullMQ,
|
|
101
|
-
|
|
75
|
+
pkg: 'bullmq',
|
|
102
76
|
});
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const active = entries.filter((e) => e.enabled).length;
|
|
77
|
+
}
|
|
78
|
+
function printBootLog(entries) {
|
|
106
79
|
const maxNameLen = Math.max(...entries.map((e) => e.name.length));
|
|
107
80
|
const lines = entries.map((entry) => {
|
|
108
81
|
const paddedName = entry.name.padEnd(maxNameLen);
|
|
109
82
|
if (entry.enabled) {
|
|
110
|
-
const mark = green('✔');
|
|
111
83
|
const detail = entry.reason ? dim('— ' + entry.reason) : dim('— ' + entry.description);
|
|
112
|
-
return ` ${
|
|
84
|
+
return ` ${green('✔')} ${bold(paddedName)} ${detail}`;
|
|
113
85
|
}
|
|
114
|
-
const mark = dim('✗');
|
|
115
86
|
const detail = dim('— ' + (entry.reason ?? entry.description));
|
|
116
|
-
return ` ${
|
|
87
|
+
return ` ${dim('✗')} ${dim(paddedName)} ${detail}`;
|
|
117
88
|
});
|
|
118
89
|
log.block('collectors (auto-detected):', lines);
|
|
119
|
-
|
|
90
|
+
}
|
|
91
|
+
export async function autoDetectCollectors() {
|
|
92
|
+
const collectors = [];
|
|
93
|
+
const entries = [];
|
|
94
|
+
await registerCoreCollectors(collectors, entries);
|
|
95
|
+
await registerOptionalCollectors(collectors, entries);
|
|
96
|
+
printBootLog(entries);
|
|
97
|
+
return { collectors, total: entries.length, active: entries.filter((e) => e.enabled).length };
|
|
120
98
|
}
|
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
import type { MetricCollector } from './collector.js';
|
|
2
|
+
/** Minimal interface for a Knex connection pool. */
|
|
3
|
+
export interface KnexPool {
|
|
4
|
+
numUsed?(): number;
|
|
5
|
+
numFree?(): number;
|
|
6
|
+
numPendingAcquires?(): number;
|
|
7
|
+
max?: number;
|
|
8
|
+
}
|
|
9
|
+
/** Extract pool stats from a Knex pool object, defaulting to 0. */
|
|
10
|
+
export declare function extractPoolMetrics(pool: Partial<KnexPool>): {
|
|
11
|
+
dbPoolUsed: number;
|
|
12
|
+
dbPoolFree: number;
|
|
13
|
+
dbPoolPending: number;
|
|
14
|
+
dbPoolMax: number;
|
|
15
|
+
};
|
|
2
16
|
/**
|
|
3
17
|
* Options for {@link dbPoolCollector}.
|
|
4
18
|
*/
|
|
@@ -15,22 +29,6 @@ export interface DbPoolCollectorOptions {
|
|
|
15
29
|
/**
|
|
16
30
|
* Monitors the Knex connection pool for a Lucid database connection.
|
|
17
31
|
*
|
|
18
|
-
* **Metrics produced:**
|
|
19
|
-
* - `dbPoolUsed` -- connections currently checked out
|
|
20
|
-
* - `dbPoolFree` -- idle connections available
|
|
21
|
-
* - `dbPoolPending` -- queries waiting for a connection
|
|
22
|
-
* - `dbPoolMax` -- maximum pool size
|
|
23
|
-
*
|
|
24
|
-
* Returns zeros if the connection or pool is unavailable.
|
|
25
|
-
*
|
|
26
32
|
* **Peer dependencies:** `@adonisjs/lucid`
|
|
27
|
-
*
|
|
28
|
-
* @example
|
|
29
|
-
* ```ts
|
|
30
|
-
* import { dbPoolCollector } from 'adonisjs-server-stats/collectors'
|
|
31
|
-
*
|
|
32
|
-
* dbPoolCollector() // monitor 'postgres'
|
|
33
|
-
* dbPoolCollector({ connectionName: 'mysql' }) // monitor 'mysql'
|
|
34
|
-
* ```
|
|
35
33
|
*/
|
|
36
34
|
export declare function dbPoolCollector(opts?: DbPoolCollectorOptions): MetricCollector;
|
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
import { appImport } from '../utils/app_import.js';
|
|
2
2
|
import { log, dim, bold } from '../utils/logger.js';
|
|
3
|
+
/** Default pool metrics returned when the pool is unavailable. */
|
|
4
|
+
const POOL_DEFAULTS = { dbPoolUsed: 0, dbPoolFree: 0, dbPoolPending: 0, dbPoolMax: 0 };
|
|
5
|
+
/** Extract pool stats from a Knex pool object, defaulting to 0. */
|
|
6
|
+
export function extractPoolMetrics(pool) {
|
|
7
|
+
return {
|
|
8
|
+
dbPoolUsed: pool.numUsed?.() ?? 0,
|
|
9
|
+
dbPoolFree: pool.numFree?.() ?? 0,
|
|
10
|
+
dbPoolPending: pool.numPendingAcquires?.() ?? 0,
|
|
11
|
+
dbPoolMax: pool.max ?? 0,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
3
14
|
/**
|
|
4
15
|
* Monitors the Knex connection pool for a Lucid database connection.
|
|
5
16
|
*
|
|
6
|
-
* **Metrics produced:**
|
|
7
|
-
* - `dbPoolUsed` -- connections currently checked out
|
|
8
|
-
* - `dbPoolFree` -- idle connections available
|
|
9
|
-
* - `dbPoolPending` -- queries waiting for a connection
|
|
10
|
-
* - `dbPoolMax` -- maximum pool size
|
|
11
|
-
*
|
|
12
|
-
* Returns zeros if the connection or pool is unavailable.
|
|
13
|
-
*
|
|
14
17
|
* **Peer dependencies:** `@adonisjs/lucid`
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```ts
|
|
18
|
-
* import { dbPoolCollector } from 'adonisjs-server-stats/collectors'
|
|
19
|
-
*
|
|
20
|
-
* dbPoolCollector() // monitor 'postgres'
|
|
21
|
-
* dbPoolCollector({ connectionName: 'mysql' }) // monitor 'mysql'
|
|
22
|
-
* ```
|
|
23
18
|
*/
|
|
24
19
|
export function dbPoolCollector(opts) {
|
|
25
20
|
const connectionName = opts?.connectionName ?? 'postgres';
|
|
@@ -34,50 +29,70 @@ export function dbPoolCollector(opts) {
|
|
|
34
29
|
},
|
|
35
30
|
async collect() {
|
|
36
31
|
try {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
warnedConnectionNotFound
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
const pool = connection.connection?.pool;
|
|
52
|
-
if (!pool) {
|
|
53
|
-
if (!warnedPoolUnavailable) {
|
|
54
|
-
warnedPoolUnavailable = true;
|
|
55
|
-
log.block(`db_pool: pool not available on connection ${bold(connectionName)}`, [
|
|
56
|
-
dim('This usually means the connection has not been established yet,'),
|
|
57
|
-
dim('or the database driver does not expose a Knex-style pool.'),
|
|
58
|
-
dim('Pool metrics will report zeros until the pool is available.'),
|
|
59
|
-
]);
|
|
60
|
-
}
|
|
61
|
-
return { dbPoolUsed: 0, dbPoolFree: 0, dbPoolPending: 0, dbPoolMax: 0 };
|
|
62
|
-
}
|
|
63
|
-
return {
|
|
64
|
-
dbPoolUsed: pool.numUsed?.() ?? 0,
|
|
65
|
-
dbPoolFree: pool.numFree?.() ?? 0,
|
|
66
|
-
dbPoolPending: pool.numPendingAcquires?.() ?? 0,
|
|
67
|
-
dbPoolMax: pool.max ?? 0,
|
|
68
|
-
};
|
|
32
|
+
return await collectPoolMetrics(connectionName, {
|
|
33
|
+
warnConnectionNotFound(alreadyWarned) {
|
|
34
|
+
if (!alreadyWarned)
|
|
35
|
+
warnedConnectionNotFound = true;
|
|
36
|
+
return warnedConnectionNotFound;
|
|
37
|
+
},
|
|
38
|
+
warnPoolUnavailable(alreadyWarned) {
|
|
39
|
+
if (!alreadyWarned)
|
|
40
|
+
warnedPoolUnavailable = true;
|
|
41
|
+
return warnedPoolUnavailable;
|
|
42
|
+
},
|
|
43
|
+
isConnectionNotFoundWarned: () => warnedConnectionNotFound,
|
|
44
|
+
isPoolUnavailableWarned: () => warnedPoolUnavailable,
|
|
45
|
+
});
|
|
69
46
|
}
|
|
70
47
|
catch {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
dim('Install it with:'),
|
|
75
|
-
` ${bold('node ace add @adonisjs/lucid')}`,
|
|
76
|
-
dim('Pool metrics will report zeros until the package is available.'),
|
|
77
|
-
]);
|
|
78
|
-
}
|
|
79
|
-
return { dbPoolUsed: 0, dbPoolFree: 0, dbPoolPending: 0, dbPoolMax: 0 };
|
|
48
|
+
warnLucidMissing(warnedLucidMissing);
|
|
49
|
+
warnedLucidMissing = true;
|
|
50
|
+
return POOL_DEFAULTS;
|
|
80
51
|
}
|
|
81
52
|
},
|
|
82
53
|
};
|
|
83
54
|
}
|
|
55
|
+
async function collectPoolMetrics(connectionName, warns) {
|
|
56
|
+
const { default: db } = await appImport('@adonisjs/lucid/services/db');
|
|
57
|
+
const connection = db.manager.get(connectionName);
|
|
58
|
+
if (!connection) {
|
|
59
|
+
warnConnectionNotFound(connectionName, warns.isConnectionNotFoundWarned());
|
|
60
|
+
warns.warnConnectionNotFound(true);
|
|
61
|
+
return POOL_DEFAULTS;
|
|
62
|
+
}
|
|
63
|
+
const pool = connection.connection?.pool;
|
|
64
|
+
if (!pool) {
|
|
65
|
+
warnPoolUnavailable(connectionName, warns.isPoolUnavailableWarned());
|
|
66
|
+
warns.warnPoolUnavailable(true);
|
|
67
|
+
return POOL_DEFAULTS;
|
|
68
|
+
}
|
|
69
|
+
return extractPoolMetrics(pool);
|
|
70
|
+
}
|
|
71
|
+
function warnConnectionNotFound(name, alreadyWarned) {
|
|
72
|
+
if (alreadyWarned)
|
|
73
|
+
return;
|
|
74
|
+
log.block(`db_pool: connection ${bold(name)} not found in db.manager`, [
|
|
75
|
+
dim('The name must match a key in your') +
|
|
76
|
+
` ${bold('config/database.ts')} ` +
|
|
77
|
+
dim('connections object.'),
|
|
78
|
+
dim('Available connections are registered at app boot — double-check spelling.'),
|
|
79
|
+
]);
|
|
80
|
+
}
|
|
81
|
+
function warnPoolUnavailable(name, alreadyWarned) {
|
|
82
|
+
if (alreadyWarned)
|
|
83
|
+
return;
|
|
84
|
+
log.block(`db_pool: pool not available on connection ${bold(name)}`, [
|
|
85
|
+
dim('This usually means the connection has not been established yet,'),
|
|
86
|
+
dim('or the database driver does not expose a Knex-style pool.'),
|
|
87
|
+
dim('Pool metrics will report zeros until the pool is available.'),
|
|
88
|
+
]);
|
|
89
|
+
}
|
|
90
|
+
function warnLucidMissing(alreadyWarned) {
|
|
91
|
+
if (alreadyWarned)
|
|
92
|
+
return;
|
|
93
|
+
log.block(`db_pool: ${bold('@adonisjs/lucid')} is not installed or failed to import`, [
|
|
94
|
+
dim('Install it with:'),
|
|
95
|
+
` ${bold('node ace add @adonisjs/lucid')}`,
|
|
96
|
+
dim('Pool metrics will report zeros until the package is available.'),
|
|
97
|
+
]);
|
|
98
|
+
}
|
|
@@ -1,54 +1,7 @@
|
|
|
1
1
|
import { LogStreamService } from '../log_stream/log_stream_service.js';
|
|
2
2
|
import type { MetricCollector } from './collector.js';
|
|
3
|
-
/**
|
|
4
|
-
* Options for {@link logCollector}.
|
|
5
|
-
*/
|
|
6
3
|
export interface LogCollectorOptions {
|
|
7
|
-
/**
|
|
8
|
-
* Path to the JSON log file to monitor.
|
|
9
|
-
*
|
|
10
|
-
* Relative to the application root. The file must contain
|
|
11
|
-
* newline-delimited JSON entries (one per line) with `level`
|
|
12
|
-
* and `time` fields (Pino format).
|
|
13
|
-
*
|
|
14
|
-
* @example `'logs/adonisjs.log'`
|
|
15
|
-
*/
|
|
16
4
|
logPath: string;
|
|
17
5
|
}
|
|
18
|
-
/**
|
|
19
|
-
* Returns the shared {@link LogStreamService} instance created by
|
|
20
|
-
* `logCollector()`, or `null` if the log collector is not configured.
|
|
21
|
-
*
|
|
22
|
-
* Useful for accessing the log stream outside of the collector
|
|
23
|
-
* (e.g. in the LogStreamProvider for live broadcasting).
|
|
24
|
-
*/
|
|
25
6
|
export declare function getLogStreamService(): LogStreamService | null;
|
|
26
|
-
/**
|
|
27
|
-
* Monitors log entries and reports rolling error/warning counts.
|
|
28
|
-
*
|
|
29
|
-
* **Two modes:**
|
|
30
|
-
* - **Zero-config (no args):** the provider auto-hooks into the AdonisJS
|
|
31
|
-
* Pino logger at boot. Entries arrive in real-time, no file path needed.
|
|
32
|
-
* - **File-based (with `logPath`):** polls a JSON log file every 2 seconds.
|
|
33
|
-
* Use this as a fallback if the auto-hook doesn't work for your setup.
|
|
34
|
-
*
|
|
35
|
-
* **Metrics produced:**
|
|
36
|
-
* - `logErrorsLast5m` -- error + fatal entries in the last 5 minutes
|
|
37
|
-
* - `logWarningsLast5m` -- warning entries in the last 5 minutes
|
|
38
|
-
* - `logEntriesLast5m` -- total entries in the last 5 minutes
|
|
39
|
-
* - `logEntriesPerMinute` -- entries per minute (averaged over 5 min)
|
|
40
|
-
*
|
|
41
|
-
* **Peer dependencies:** none
|
|
42
|
-
*
|
|
43
|
-
* @example
|
|
44
|
-
* ```ts
|
|
45
|
-
* import { logCollector } from 'adonisjs-server-stats/collectors'
|
|
46
|
-
*
|
|
47
|
-
* // Zero-config — auto-hooks into AdonisJS logger (recommended)
|
|
48
|
-
* logCollector()
|
|
49
|
-
*
|
|
50
|
-
* // File-based fallback
|
|
51
|
-
* logCollector({ logPath: 'logs/adonisjs.log' })
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
54
7
|
export declare function logCollector(opts?: LogCollectorOptions): MetricCollector;
|
|
@@ -2,44 +2,33 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import { LogStreamService } from '../log_stream/log_stream_service.js';
|
|
3
3
|
import { log, dim, bold } from '../utils/logger.js';
|
|
4
4
|
let sharedLogStream = null;
|
|
5
|
-
/**
|
|
6
|
-
* Returns the shared {@link LogStreamService} instance created by
|
|
7
|
-
* `logCollector()`, or `null` if the log collector is not configured.
|
|
8
|
-
*
|
|
9
|
-
* Useful for accessing the log stream outside of the collector
|
|
10
|
-
* (e.g. in the LogStreamProvider for live broadcasting).
|
|
11
|
-
*/
|
|
12
5
|
export function getLogStreamService() {
|
|
13
6
|
return sharedLogStream;
|
|
14
7
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
* // File-based fallback
|
|
40
|
-
* logCollector({ logPath: 'logs/adonisjs.log' })
|
|
41
|
-
* ```
|
|
42
|
-
*/
|
|
8
|
+
function warnMissingFile(logPath, alreadyWarned) {
|
|
9
|
+
if (alreadyWarned)
|
|
10
|
+
return true;
|
|
11
|
+
log.warn(`Log file not found: ${bold(logPath)}`);
|
|
12
|
+
log.block('The log collector will keep retrying, but no metrics will appear until the file exists.', [
|
|
13
|
+
dim('Make sure the path is correct and your app is writing logs there.'),
|
|
14
|
+
dim('The file must contain newline-delimited JSON with') +
|
|
15
|
+
` ${bold('level')} ` +
|
|
16
|
+
dim('and') +
|
|
17
|
+
` ${bold('time')} ` +
|
|
18
|
+
dim('fields (Pino format).'),
|
|
19
|
+
]);
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
function warnStartFailure(error, logPath, alreadyWarned) {
|
|
23
|
+
if (alreadyWarned)
|
|
24
|
+
return true;
|
|
25
|
+
log.warn(`Log collector failed to start: ${bold(String(error))}`);
|
|
26
|
+
log.block('The log collector will not produce metrics until this is resolved.', [
|
|
27
|
+
dim('Configured log path:') + ` ${bold(logPath)}`,
|
|
28
|
+
dim('Check file permissions and ensure the directory exists.'),
|
|
29
|
+
]);
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
43
32
|
export function logCollector(opts) {
|
|
44
33
|
if (sharedLogStream) {
|
|
45
34
|
console.warn('[server-stats] logCollector() called again — stopping previous LogStreamService instance');
|
|
@@ -47,8 +36,8 @@ export function logCollector(opts) {
|
|
|
47
36
|
}
|
|
48
37
|
const service = new LogStreamService(opts?.logPath);
|
|
49
38
|
sharedLogStream = service;
|
|
50
|
-
let
|
|
51
|
-
let
|
|
39
|
+
let warnedMissing = false;
|
|
40
|
+
let warnedStart = false;
|
|
52
41
|
return {
|
|
53
42
|
name: 'log',
|
|
54
43
|
label: opts?.logPath ? `log — file: ${opts.logPath}` : 'log — pino stream (zero-config)',
|
|
@@ -59,35 +48,17 @@ export function logCollector(opts) {
|
|
|
59
48
|
};
|
|
60
49
|
},
|
|
61
50
|
async start() {
|
|
62
|
-
if (opts?.logPath)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
` ${bold('time')} ` +
|
|
73
|
-
dim('fields (Pino format).'),
|
|
74
|
-
]);
|
|
75
|
-
}
|
|
76
|
-
try {
|
|
77
|
-
await service.start();
|
|
78
|
-
}
|
|
79
|
-
catch (error) {
|
|
80
|
-
if (!warnedStartFailure) {
|
|
81
|
-
warnedStartFailure = true;
|
|
82
|
-
log.warn(`Log collector failed to start: ${bold(String(error))}`);
|
|
83
|
-
log.block('The log collector will not produce metrics until this is resolved.', [
|
|
84
|
-
dim('Configured log path:') + ` ${bold(opts.logPath)}`,
|
|
85
|
-
dim('Check file permissions and ensure the directory exists.'),
|
|
86
|
-
]);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
51
|
+
if (!opts?.logPath)
|
|
52
|
+
return;
|
|
53
|
+
if (!existsSync(opts.logPath)) {
|
|
54
|
+
warnedMissing = warnMissingFile(opts.logPath, warnedMissing);
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
await service.start();
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
warnedStart = warnStartFailure(error, opts.logPath, warnedStart);
|
|
89
61
|
}
|
|
90
|
-
// Stream mode: no startup needed — entries arrive via ingest()
|
|
91
62
|
},
|
|
92
63
|
stop() {
|
|
93
64
|
service.stop();
|
|
@@ -39,28 +39,8 @@ export interface QueueCollectorOptions {
|
|
|
39
39
|
/**
|
|
40
40
|
* Monitors a BullMQ job queue for active, waiting, delayed, and failed jobs.
|
|
41
41
|
*
|
|
42
|
-
* Creates a temporary BullMQ `Queue` instance each tick to fetch counts,
|
|
43
|
-
* then immediately closes it.
|
|
44
|
-
*
|
|
45
|
-
* **Metrics produced:**
|
|
46
|
-
* - `queueActive` -- jobs being processed
|
|
47
|
-
* - `queueWaiting` -- jobs waiting for a worker
|
|
48
|
-
* - `queueDelayed` -- jobs scheduled for the future
|
|
49
|
-
* - `queueFailed` -- permanently failed jobs
|
|
50
|
-
* - `queueWorkerCount` -- connected worker processes
|
|
51
|
-
*
|
|
52
42
|
* Returns zeros if BullMQ is unavailable or the queue cannot be reached.
|
|
53
43
|
*
|
|
54
44
|
* **Peer dependencies:** `bullmq`
|
|
55
|
-
*
|
|
56
|
-
* @example
|
|
57
|
-
* ```ts
|
|
58
|
-
* import { queueCollector } from 'adonisjs-server-stats/collectors'
|
|
59
|
-
*
|
|
60
|
-
* queueCollector({
|
|
61
|
-
* queueName: 'default',
|
|
62
|
-
* connection: { host: 'localhost', port: 6379, password: 'secret' },
|
|
63
|
-
* })
|
|
64
|
-
* ```
|
|
65
45
|
*/
|
|
66
46
|
export declare function queueCollector(opts: QueueCollectorOptions): MetricCollector;
|