adonisjs-server-stats 1.4.0 → 1.5.2
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 +272 -142
- package/dist/configure.d.ts.map +1 -1
- package/dist/src/controller/debug_controller.d.ts +2 -2
- package/dist/src/controller/debug_controller.d.ts.map +1 -1
- package/dist/src/controller/server_stats_controller.d.ts +1 -1
- package/dist/src/controller/server_stats_controller.d.ts.map +1 -1
- package/dist/src/dashboard/chart_aggregator.d.ts.map +1 -1
- package/dist/src/dashboard/chart_aggregator.js +8 -8
- package/dist/src/dashboard/dashboard_controller.d.ts +12 -97
- package/dist/src/dashboard/dashboard_controller.d.ts.map +1 -1
- package/dist/src/dashboard/dashboard_controller.js +244 -522
- package/dist/src/dashboard/dashboard_routes.d.ts.map +1 -1
- package/dist/src/dashboard/dashboard_routes.js +7 -2
- package/dist/src/dashboard/dashboard_store.d.ts +6 -3
- package/dist/src/dashboard/dashboard_store.d.ts.map +1 -1
- package/dist/src/dashboard/dashboard_store.js +54 -78
- package/dist/src/dashboard/integrations/cache_inspector.d.ts.map +1 -1
- package/dist/src/dashboard/integrations/queue_inspector.d.ts.map +1 -1
- package/dist/src/dashboard/migrator.d.ts.map +1 -1
- package/dist/src/dashboard/migrator.js +3 -1
- package/dist/src/dashboard/models/stats_event.d.ts +1 -1
- package/dist/src/dashboard/models/stats_event.d.ts.map +1 -1
- package/dist/src/dashboard/models/stats_query.d.ts +1 -1
- package/dist/src/dashboard/models/stats_query.d.ts.map +1 -1
- package/dist/src/dashboard/models/stats_request.d.ts +2 -2
- package/dist/src/dashboard/models/stats_request.d.ts.map +1 -1
- package/dist/src/dashboard/models/stats_request.js +1 -1
- package/dist/src/dashboard/models/stats_trace.d.ts +1 -1
- package/dist/src/dashboard/models/stats_trace.d.ts.map +1 -1
- package/dist/src/debug/debug_store.d.ts +6 -6
- package/dist/src/debug/debug_store.d.ts.map +1 -1
- package/dist/src/debug/debug_store.js +10 -10
- package/dist/src/debug/email_collector.d.ts +0 -9
- package/dist/src/debug/email_collector.d.ts.map +1 -1
- package/dist/src/debug/email_collector.js +6 -28
- package/dist/src/debug/event_collector.d.ts +1 -1
- package/dist/src/debug/event_collector.d.ts.map +1 -1
- package/dist/src/debug/event_collector.js +17 -17
- package/dist/src/debug/query_collector.d.ts +1 -1
- package/dist/src/debug/query_collector.d.ts.map +1 -1
- package/dist/src/debug/query_collector.js +13 -14
- package/dist/src/debug/ring_buffer.d.ts.map +1 -1
- package/dist/src/debug/route_inspector.d.ts +1 -1
- package/dist/src/debug/route_inspector.d.ts.map +1 -1
- package/dist/src/debug/route_inspector.js +12 -12
- package/dist/src/debug/trace_collector.d.ts.map +1 -1
- package/dist/src/debug/trace_collector.js +6 -5
- package/dist/src/edge/client/dashboard.css +516 -171
- package/dist/src/edge/client/dashboard.js +2756 -1662
- package/dist/src/edge/client/debug-panel.css +476 -133
- package/dist/src/edge/client/debug-panel.js +1496 -1043
- package/dist/src/edge/client/stats-bar.css +64 -30
- package/dist/src/edge/client/stats-bar.js +598 -319
- package/dist/src/edge/plugin.d.ts +1 -1
- package/dist/src/edge/plugin.d.ts.map +1 -1
- package/dist/src/edge/plugin.js +41 -59
- package/dist/src/edge/views/stats-bar.edge +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/middleware/request_tracking_middleware.d.ts +4 -4
- package/dist/src/middleware/request_tracking_middleware.d.ts.map +1 -1
- package/dist/src/middleware/request_tracking_middleware.js +7 -6
- package/dist/src/prometheus/prometheus_collector.d.ts +1 -1
- package/dist/src/prometheus/prometheus_collector.d.ts.map +1 -1
- package/dist/src/provider/server_stats_provider.d.ts +1 -1
- package/dist/src/provider/server_stats_provider.d.ts.map +1 -1
- package/dist/src/provider/server_stats_provider.js +33 -32
- package/dist/src/types.d.ts +2 -2
- package/dist/src/utils/json_helpers.d.ts +8 -0
- package/dist/src/utils/json_helpers.d.ts.map +1 -0
- package/dist/src/utils/json_helpers.js +21 -0
- package/dist/src/utils/mail_helpers.d.ts +13 -0
- package/dist/src/utils/mail_helpers.d.ts.map +1 -0
- package/dist/src/utils/mail_helpers.js +26 -0
- package/dist/src/utils/math_helpers.d.ts +8 -0
- package/dist/src/utils/math_helpers.d.ts.map +1 -0
- package/dist/src/utils/math_helpers.js +11 -0
- package/dist/src/utils/time_helpers.d.ts +12 -0
- package/dist/src/utils/time_helpers.d.ts.map +1 -0
- package/dist/src/utils/time_helpers.js +32 -0
- package/dist/src/utils/transmit_client.d.ts +9 -0
- package/dist/src/utils/transmit_client.d.ts.map +1 -0
- package/dist/src/utils/transmit_client.js +20 -0
- package/package.json +35 -29
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/edge/plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/edge/plugin.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAKpD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,iBAAiB,IACrD,MAAM,GAAG,UAmHlB"}
|
package/dist/src/edge/plugin.js
CHANGED
|
@@ -1,26 +1,10 @@
|
|
|
1
|
-
import { readFileSync } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { Template } from 'edge.js';
|
|
5
|
+
import { loadTransmitClient } from '../utils/transmit_client.js';
|
|
6
6
|
const DIR = dirname(fileURLToPath(import.meta.url));
|
|
7
|
-
const read = (rel) => readFileSync(join(DIR, rel),
|
|
8
|
-
/**
|
|
9
|
-
* Try to locate and read the @adonisjs/transmit-client build file.
|
|
10
|
-
* Returns the file contents wrapped to expose `window.Transmit`, or
|
|
11
|
-
* an empty string if the package is not installed.
|
|
12
|
-
*/
|
|
13
|
-
function loadTransmitClient() {
|
|
14
|
-
try {
|
|
15
|
-
const req = createRequire(join(process.cwd(), "package.json"));
|
|
16
|
-
const clientPath = req.resolve("@adonisjs/transmit-client/build/index.js");
|
|
17
|
-
const src = readFileSync(clientPath, "utf-8");
|
|
18
|
-
return `(function(){var __exports={};(function(){${src.replace(/^export\s*\{[^}]*\}\s*;?\s*$/m, "")}\n__exports.Transmit=Transmit;})();window.Transmit=__exports.Transmit;})()`;
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
return "";
|
|
22
|
-
}
|
|
23
|
-
}
|
|
7
|
+
const read = (rel) => readFileSync(join(DIR, rel), 'utf-8');
|
|
24
8
|
/**
|
|
25
9
|
* Edge plugin that registers the `@serverStats()` tag.
|
|
26
10
|
*
|
|
@@ -43,64 +27,62 @@ function loadTransmitClient() {
|
|
|
43
27
|
export function edgePluginServerStats(config) {
|
|
44
28
|
return (edge) => {
|
|
45
29
|
// Mount Edge views under the `ss` disk (needed for @include resolution)
|
|
46
|
-
edge.mount(
|
|
30
|
+
edge.mount('ss', join(DIR, 'views'));
|
|
47
31
|
// Read client assets once at boot
|
|
48
|
-
const css = read(
|
|
49
|
-
const js = read(
|
|
50
|
-
const endpoint = typeof config.endpoint ===
|
|
51
|
-
? config.endpoint
|
|
52
|
-
: "/admin/api/server-stats";
|
|
32
|
+
const css = read('client/stats-bar.css');
|
|
33
|
+
const js = read('client/stats-bar.js');
|
|
34
|
+
const endpoint = typeof config.endpoint === 'string' ? config.endpoint : '/admin/api/server-stats';
|
|
53
35
|
const intervalMs = config.intervalMs || 3000;
|
|
54
36
|
const showDebug = !!config.devToolbar?.enabled;
|
|
55
37
|
// Badge groups for the Edge template
|
|
56
38
|
const groups = [
|
|
57
39
|
// Process
|
|
58
40
|
[
|
|
59
|
-
{ id:
|
|
60
|
-
{ id:
|
|
61
|
-
{ id:
|
|
62
|
-
{ id:
|
|
41
|
+
{ id: 'node', label: 'NODE' },
|
|
42
|
+
{ id: 'up', label: 'UP' },
|
|
43
|
+
{ id: 'cpu', label: 'CPU' },
|
|
44
|
+
{ id: 'evt', label: 'EVT' },
|
|
63
45
|
],
|
|
64
46
|
// Memory
|
|
65
47
|
[
|
|
66
|
-
{ id:
|
|
67
|
-
{ id:
|
|
68
|
-
{ id:
|
|
48
|
+
{ id: 'mem', label: 'HEAP' },
|
|
49
|
+
{ id: 'rss', label: 'RSS' },
|
|
50
|
+
{ id: 'sys', label: 'SYS' },
|
|
69
51
|
],
|
|
70
52
|
// HTTP
|
|
71
53
|
[
|
|
72
|
-
{ id:
|
|
73
|
-
{ id:
|
|
74
|
-
{ id:
|
|
75
|
-
{ id:
|
|
54
|
+
{ id: 'rps', label: 'REQ/s' },
|
|
55
|
+
{ id: 'avg', label: 'AVG' },
|
|
56
|
+
{ id: 'err', label: 'ERR' },
|
|
57
|
+
{ id: 'conn', label: 'CONN' },
|
|
76
58
|
],
|
|
77
59
|
// DB
|
|
78
|
-
[{ id:
|
|
60
|
+
[{ id: 'db', label: 'DB' }],
|
|
79
61
|
// Redis
|
|
80
62
|
[
|
|
81
|
-
{ id:
|
|
82
|
-
{ id:
|
|
83
|
-
{ id:
|
|
84
|
-
{ id:
|
|
63
|
+
{ id: 'redis', label: 'REDIS' },
|
|
64
|
+
{ id: 'rmem', label: 'MEM' },
|
|
65
|
+
{ id: 'rkeys', label: 'KEYS' },
|
|
66
|
+
{ id: 'rhit', label: 'HIT' },
|
|
85
67
|
],
|
|
86
68
|
// Queue
|
|
87
69
|
[
|
|
88
|
-
{ id:
|
|
89
|
-
{ id:
|
|
70
|
+
{ id: 'q', label: 'Q' },
|
|
71
|
+
{ id: 'workers', label: 'WORKERS' },
|
|
90
72
|
],
|
|
91
73
|
// App
|
|
92
74
|
[
|
|
93
|
-
{ id:
|
|
94
|
-
{ id:
|
|
95
|
-
{ id:
|
|
75
|
+
{ id: 'users', label: 'USERS' },
|
|
76
|
+
{ id: 'hooks', label: 'HOOKS' },
|
|
77
|
+
{ id: 'mail', label: 'MAIL' },
|
|
96
78
|
],
|
|
97
79
|
// Logs
|
|
98
80
|
[
|
|
99
|
-
{ id:
|
|
100
|
-
{ id:
|
|
81
|
+
{ id: 'logerr', label: 'LOG ERR' },
|
|
82
|
+
{ id: 'lograte', label: 'LOG/m' },
|
|
101
83
|
],
|
|
102
84
|
// Debug (conditional)
|
|
103
|
-
...(showDebug ? [[{ id:
|
|
85
|
+
...(showDebug ? [[{ id: 'dbg-queries', label: 'QRY' }]] : []),
|
|
104
86
|
];
|
|
105
87
|
const state = {
|
|
106
88
|
css,
|
|
@@ -111,25 +93,25 @@ export function edgePluginServerStats(config) {
|
|
|
111
93
|
groups,
|
|
112
94
|
};
|
|
113
95
|
if (showDebug) {
|
|
114
|
-
state.debugCss = read(
|
|
115
|
-
state.debugJs = read(
|
|
116
|
-
state.logsEndpoint =
|
|
96
|
+
state.debugCss = read('client/debug-panel.css');
|
|
97
|
+
state.debugJs = read('client/debug-panel.js');
|
|
98
|
+
state.logsEndpoint = '/admin/api/debug/logs';
|
|
117
99
|
state.customPanes = config.devToolbar?.panes || [];
|
|
118
100
|
state.showTracing = !!config.devToolbar?.tracing;
|
|
119
101
|
state.dashboardPath = config.devToolbar?.dashboard
|
|
120
|
-
?
|
|
102
|
+
? config.devToolbar.dashboardPath || '/__stats'
|
|
121
103
|
: null;
|
|
122
|
-
state.transmitClient = loadTransmitClient();
|
|
104
|
+
state.transmitClient = loadTransmitClient(join(process.cwd(), 'package.json'));
|
|
123
105
|
}
|
|
124
106
|
// Pre-render via Template directly — bypasses edge.createRenderer() which
|
|
125
107
|
// would re-run #executePlugins and cause infinite recursion.
|
|
126
108
|
const template = new Template(edge.compiler, edge.globals, {}, edge.processor);
|
|
127
|
-
const html = template.render(
|
|
109
|
+
const html = template.render('ss::stats-bar', state);
|
|
128
110
|
const escaped = JSON.stringify(html);
|
|
129
111
|
// Track whether shouldShow is configured (controls render-time guard)
|
|
130
112
|
const hasShouldShow = !!config.shouldShow;
|
|
131
113
|
edge.registerTag({
|
|
132
|
-
tagName:
|
|
114
|
+
tagName: 'serverStats',
|
|
133
115
|
block: false,
|
|
134
116
|
seekable: true,
|
|
135
117
|
compile(_parser, buffer, token) {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<div id="ss-bar" class="ss-bar" data-endpoint="{{ endpoint }}" data-interval="{{ intervalMs }}">
|
|
6
6
|
<div class="ss-bar-left">
|
|
7
7
|
@if(showDebug)
|
|
8
|
-
<button type="button" id="ss-dbg-wrench" class="ss-dbg-btn" title="Open debug panel"
|
|
8
|
+
<button type="button" id="ss-dbg-wrench" class="ss-dbg-btn" title="Open debug panel">🔍 Open debug panel</button>
|
|
9
9
|
@end
|
|
10
10
|
<div id="ss-dot" class="ss-dot"></div>
|
|
11
11
|
</div>
|
package/dist/src/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { RequestMetrics } from './engine/request_metrics.js';
|
|
|
4
4
|
export { trace } from './debug/trace_collector.js';
|
|
5
5
|
export { DashboardStore } from './dashboard/dashboard_store.js';
|
|
6
6
|
export type { MetricCollector } from './collectors/collector.js';
|
|
7
|
-
export type { MetricValue, ServerStats, ServerStatsConfig, LogStats, DevToolbarOptions } from './types.js';
|
|
7
|
+
export type { MetricValue, ServerStats, ServerStatsConfig, LogStats, DevToolbarOptions, } from './types.js';
|
|
8
8
|
export type { DebugPane, DebugPaneColumn, DebugPaneFormatType, DebugPaneSearch, BadgeColor, QueryRecord, EventRecord, EmailRecord, RouteRecord, TraceSpan, TraceRecord, DevToolbarConfig, } from './debug/types.js';
|
|
9
9
|
export type { RequestFilters, QueryFilters, EventFilters, EmailFilters, LogFilters, TraceFilters, PaginatedResult, } from './dashboard/dashboard_store.js';
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAC/D,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAChE,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAC/D,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAChE,YAAY,EACV,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,QAAQ,EACR,iBAAiB,GAClB,MAAM,YAAY,CAAA;AACnB,YAAY,EACV,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,UAAU,EACV,WAAW,EACX,WAAW,EACX,WAAW,EACX,WAAW,EACX,SAAS,EACT,WAAW,EACX,gBAAgB,GACjB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACV,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,eAAe,GAChB,MAAM,gCAAgC,CAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
1
|
+
import type { TraceCollector } from '../debug/trace_collector.js';
|
|
2
|
+
import type { TraceRecord } from '../debug/types.js';
|
|
3
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
4
|
+
import type { NextFn } from '@adonisjs/core/types/http';
|
|
5
5
|
/**
|
|
6
6
|
* Returns true if the current async context is inside an excluded request
|
|
7
7
|
* (e.g. a debug panel polling request). Used by collectors to skip
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request_tracking_middleware.d.ts","sourceRoot":"","sources":["../../../src/middleware/request_tracking_middleware.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"request_tracking_middleware.d.ts","sourceRoot":"","sources":["../../../src/middleware/request_tracking_middleware.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAA;AASvD;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAOD,wBAAgB,aAAa,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,IAAI,QAE/D;AAOD,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI,QAEjE;AAQD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,QAEnD;AASD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAErD;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,WAAW,CAAA;CACpB;AAQD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC,GAAG,IAAI,QAEpF;AAED,MAAM,CAAC,OAAO,OAAO,yBAAyB;IACtC,MAAM,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM;CAuE5C"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from
|
|
2
|
-
import { performance } from
|
|
3
|
-
import { getRequestMetrics } from
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { performance } from 'node:perf_hooks';
|
|
3
|
+
import { getRequestMetrics } from '../collectors/http_collector.js';
|
|
4
4
|
/**
|
|
5
5
|
* AsyncLocalStorage that marks the current request as "excluded" from
|
|
6
6
|
* debug collection. Checked by QueryCollector and EventCollector to
|
|
@@ -70,7 +70,8 @@ export default class RequestTrackingMiddleware {
|
|
|
70
70
|
// runs BEFORE router middleware like initialize_auth_middleware and
|
|
71
71
|
// silentAuth — so ctx.auth isn't populated yet. The function is called
|
|
72
72
|
// at Edge render time (inside the controller), when auth is available.
|
|
73
|
-
if (shouldShowFn && typeof ctx.view?.share ===
|
|
73
|
+
if (shouldShowFn && typeof ctx.view?.share === 'function') {
|
|
74
|
+
;
|
|
74
75
|
ctx.view.share({
|
|
75
76
|
__ssShowFn: () => {
|
|
76
77
|
try {
|
|
@@ -85,8 +86,8 @@ export default class RequestTrackingMiddleware {
|
|
|
85
86
|
// Skip tracing and dashboard persistence for the debug panel's own requests
|
|
86
87
|
// (e.g. /admin/api/debug/*, /admin/api/server-stats) so they don't flood
|
|
87
88
|
// the timeline. HTTP metrics (req/s, avg latency) are still recorded.
|
|
88
|
-
const skipTracing = excludedPrefixes.length > 0
|
|
89
|
-
|
|
89
|
+
const skipTracing = excludedPrefixes.length > 0 &&
|
|
90
|
+
excludedPrefixes.some((prefix) => requestUrl.startsWith(prefix));
|
|
90
91
|
const runRequest = async () => {
|
|
91
92
|
try {
|
|
92
93
|
await next();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ConfigProvider } from '@adonisjs/core/types';
|
|
2
1
|
import type { ServerStats } from '../types.js';
|
|
2
|
+
import type { ConfigProvider } from '@adonisjs/core/types';
|
|
3
3
|
export declare function serverStatsCollector(): ConfigProvider<any>;
|
|
4
4
|
export declare const ServerStatsCollector: {
|
|
5
5
|
instance: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prometheus_collector.d.ts","sourceRoot":"","sources":["../../../src/prometheus/prometheus_collector.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"prometheus_collector.d.ts","sourceRoot":"","sources":["../../../src/prometheus/prometheus_collector.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAE1D,wBAAgB,oBAAoB,IAAI,cAAc,CAAC,GAAG,CAAC,CA6M1D;AAED,eAAO,MAAM,oBAAoB,EAAE;IACjC,QAAQ,EAAE;QAAE,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAA;CAG/D,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server_stats_provider.d.ts","sourceRoot":"","sources":["../../../src/provider/server_stats_provider.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server_stats_provider.d.ts","sourceRoot":"","sources":["../../../src/provider/server_stats_provider.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAE9D,MAAM,CAAC,OAAO,OAAO,mBAAmB;IAY1B,SAAS,CAAC,GAAG,EAAE,kBAAkB;IAX7C,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,cAAc,CAA8B;IACpD,OAAO,CAAC,mBAAmB,CAAmC;IAC9D,OAAO,CAAC,kBAAkB,CAAgC;IAC1D,OAAO,CAAC,uBAAuB,CAA8C;IAC7E,OAAO,CAAC,mBAAmB,CAA6C;IACxE,OAAO,CAAC,WAAW,CAAsB;IACzC,OAAO,CAAC,UAAU,CAA8C;gBAE1C,GAAG,EAAE,kBAAkB;IAEvC,IAAI;IAkCJ,KAAK;YA+EG,eAAe;IAmF7B;;;;;;OAMG;YACW,cAAc;IAoGtB,QAAQ;CAwCf"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { registerDashboardRoutes } from '../dashboard/dashboard_routes.js';
|
|
2
|
+
import { DashboardStore } from '../dashboard/dashboard_store.js';
|
|
3
|
+
import { DebugStore } from '../debug/debug_store.js';
|
|
4
|
+
import { StatsEngine } from '../engine/stats_engine.js';
|
|
5
|
+
import { LogStreamService } from '../log_stream/log_stream_service.js';
|
|
6
|
+
import { setShouldShow, setTraceCollector, setDashboardPath, setExcludedPrefixes, setOnRequestComplete, } from '../middleware/request_tracking_middleware.js';
|
|
7
7
|
export default class ServerStatsProvider {
|
|
8
8
|
app;
|
|
9
9
|
intervalId = null;
|
|
@@ -20,7 +20,7 @@ export default class ServerStatsProvider {
|
|
|
20
20
|
this.app = app;
|
|
21
21
|
}
|
|
22
22
|
async boot() {
|
|
23
|
-
const config = this.app.config.get(
|
|
23
|
+
const config = this.app.config.get('server_stats');
|
|
24
24
|
if (!config)
|
|
25
25
|
return;
|
|
26
26
|
// Wire up the per-request shouldShow callback
|
|
@@ -32,7 +32,7 @@ export default class ServerStatsProvider {
|
|
|
32
32
|
const toolbarConfig = config.devToolbar;
|
|
33
33
|
if (toolbarConfig?.enabled && toolbarConfig.dashboard && !this.app.inProduction) {
|
|
34
34
|
try {
|
|
35
|
-
const router = await this.app.container.make(
|
|
35
|
+
const router = await this.app.container.make('router');
|
|
36
36
|
const dashPath = toolbarConfig.dashboardPath ?? '/__stats';
|
|
37
37
|
registerDashboardRoutes(router, dashPath, () => this.dashboardController, config.shouldShow);
|
|
38
38
|
}
|
|
@@ -43,8 +43,8 @@ export default class ServerStatsProvider {
|
|
|
43
43
|
if (!this.app.usingEdgeJS)
|
|
44
44
|
return;
|
|
45
45
|
try {
|
|
46
|
-
const edge = await import(
|
|
47
|
-
const { edgePluginServerStats } = await import(
|
|
46
|
+
const edge = await import('edge.js');
|
|
47
|
+
const { edgePluginServerStats } = await import('../edge/plugin.js');
|
|
48
48
|
edge.default.use(edgePluginServerStats(config));
|
|
49
49
|
}
|
|
50
50
|
catch {
|
|
@@ -52,14 +52,13 @@ export default class ServerStatsProvider {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
async ready() {
|
|
55
|
-
const config = this.app.config.get(
|
|
55
|
+
const config = this.app.config.get('server_stats');
|
|
56
56
|
if (!config)
|
|
57
57
|
return;
|
|
58
58
|
if (this.app.inTest && config.skipInTest !== false)
|
|
59
59
|
return;
|
|
60
60
|
this.engine = new StatsEngine(config.collectors);
|
|
61
|
-
|
|
62
|
-
this.app.container.singleton("server_stats.engine", () => this.engine);
|
|
61
|
+
this.app.container.singleton('server_stats.engine', () => this.engine);
|
|
63
62
|
await this.engine.start();
|
|
64
63
|
// Dev toolbar setup
|
|
65
64
|
const toolbarConfig = config.devToolbar;
|
|
@@ -80,7 +79,8 @@ export default class ServerStatsProvider {
|
|
|
80
79
|
});
|
|
81
80
|
// Exclude the stats endpoint and user-specified prefixes from tracing
|
|
82
81
|
// so the debug panel's own polling doesn't flood the timeline
|
|
83
|
-
const
|
|
82
|
+
const defaultExcludes = ['/admin/api/debug', '/admin/api/server-stats'];
|
|
83
|
+
const prefixes = [...(toolbarConfig.excludeFromTracing ?? defaultExcludes)];
|
|
84
84
|
if (typeof config.endpoint === 'string') {
|
|
85
85
|
prefixes.push(config.endpoint);
|
|
86
86
|
}
|
|
@@ -89,9 +89,9 @@ export default class ServerStatsProvider {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
let transmit = null;
|
|
92
|
-
if (config.transport ===
|
|
92
|
+
if (config.transport === 'transmit') {
|
|
93
93
|
try {
|
|
94
|
-
transmit = await this.app.container.make(
|
|
94
|
+
transmit = await this.app.container.make('transmit');
|
|
95
95
|
}
|
|
96
96
|
catch {
|
|
97
97
|
// Transmit not installed — skip broadcasting
|
|
@@ -99,7 +99,7 @@ export default class ServerStatsProvider {
|
|
|
99
99
|
}
|
|
100
100
|
let prometheusCollector = null;
|
|
101
101
|
try {
|
|
102
|
-
const mod = await import(
|
|
102
|
+
const mod = await import('../prometheus/prometheus_collector.js');
|
|
103
103
|
prometheusCollector = mod.ServerStatsCollector.instance;
|
|
104
104
|
}
|
|
105
105
|
catch {
|
|
@@ -123,19 +123,19 @@ export default class ServerStatsProvider {
|
|
|
123
123
|
}
|
|
124
124
|
async setupDevToolbar(toolbarConfig) {
|
|
125
125
|
this.debugStore = new DebugStore(toolbarConfig);
|
|
126
|
-
|
|
127
|
-
this.app.container.singleton("debug.store", () => this.debugStore);
|
|
126
|
+
this.app.container.singleton('debug.store', () => this.debugStore);
|
|
128
127
|
// Load persisted data before starting collectors
|
|
129
128
|
if (toolbarConfig.persistDebugData) {
|
|
130
|
-
this.persistPath =
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
this.persistPath =
|
|
130
|
+
typeof toolbarConfig.persistDebugData === 'string'
|
|
131
|
+
? this.app.makePath(toolbarConfig.persistDebugData)
|
|
132
|
+
: this.app.makePath('.adonisjs', 'server-stats', 'debug-data.json');
|
|
133
133
|
await this.debugStore.loadFromDisk(this.persistPath);
|
|
134
134
|
}
|
|
135
135
|
// Get the emitter
|
|
136
136
|
let emitter = null;
|
|
137
137
|
try {
|
|
138
|
-
emitter = await this.app.container.make(
|
|
138
|
+
emitter = await this.app.container.make('emitter');
|
|
139
139
|
}
|
|
140
140
|
catch {
|
|
141
141
|
// Emitter not available
|
|
@@ -143,7 +143,7 @@ export default class ServerStatsProvider {
|
|
|
143
143
|
// Get the router
|
|
144
144
|
let router = null;
|
|
145
145
|
try {
|
|
146
|
-
router = await this.app.container.make(
|
|
146
|
+
router = await this.app.container.make('router');
|
|
147
147
|
}
|
|
148
148
|
catch {
|
|
149
149
|
// Router not available
|
|
@@ -167,13 +167,13 @@ export default class ServerStatsProvider {
|
|
|
167
167
|
// ── Transmit broadcasting for debug panel live updates ────────
|
|
168
168
|
let debugTransmit = null;
|
|
169
169
|
try {
|
|
170
|
-
debugTransmit = await this.app.container.make(
|
|
170
|
+
debugTransmit = await this.app.container.make('transmit');
|
|
171
171
|
}
|
|
172
172
|
catch {
|
|
173
173
|
// Transmit not installed — debug panel will use polling
|
|
174
174
|
}
|
|
175
175
|
if (debugTransmit) {
|
|
176
|
-
const debugChannel =
|
|
176
|
+
const debugChannel = 'server-stats/debug';
|
|
177
177
|
const pendingTypes = new Set();
|
|
178
178
|
this.debugStore.onNewItem((type) => {
|
|
179
179
|
// Debounce: coalesce rapid events into a single broadcast
|
|
@@ -224,14 +224,15 @@ export default class ServerStatsProvider {
|
|
|
224
224
|
throw err;
|
|
225
225
|
}
|
|
226
226
|
// Bind to container
|
|
227
|
-
|
|
227
|
+
;
|
|
228
|
+
this.app.container.singleton('dashboard.store', () => this.dashboardStore);
|
|
228
229
|
// Set dashboard path in middleware for self-exclusion
|
|
229
230
|
setDashboardPath(toolbarConfig.dashboardPath);
|
|
230
231
|
// Create the controller — this makes the routes registered in boot() functional
|
|
231
|
-
const DashboardControllerClass = (await import(
|
|
232
|
+
const DashboardControllerClass = (await import('../dashboard/dashboard_controller.js')).default;
|
|
232
233
|
this.dashboardController = new DashboardControllerClass(this.dashboardStore, this.debugStore, this.app);
|
|
233
234
|
// ── Log piping ────────────────────────────────────────────────
|
|
234
|
-
const logPath = this.app.makePath(
|
|
235
|
+
const logPath = this.app.makePath('logs', 'adonisjs.log');
|
|
235
236
|
this.dashboardLogStream = new LogStreamService(logPath, (entry) => {
|
|
236
237
|
this.dashboardStore?.recordLog(entry);
|
|
237
238
|
});
|
|
@@ -271,18 +272,18 @@ export default class ServerStatsProvider {
|
|
|
271
272
|
// ── Transmit streaming for real-time dashboard updates ────────
|
|
272
273
|
let transmit = null;
|
|
273
274
|
try {
|
|
274
|
-
transmit = await this.app.container.make(
|
|
275
|
+
transmit = await this.app.container.make('transmit');
|
|
275
276
|
}
|
|
276
277
|
catch {
|
|
277
278
|
// Transmit not installed — skip real-time updates
|
|
278
279
|
}
|
|
279
280
|
if (transmit) {
|
|
280
|
-
const dashChannel =
|
|
281
|
+
const dashChannel = 'server-stats/dashboard';
|
|
281
282
|
this.dashboardBroadcastTimer = setInterval(async () => {
|
|
282
283
|
try {
|
|
283
284
|
if (!dashStore.isReady())
|
|
284
285
|
return;
|
|
285
|
-
const overview = await dashStore.getOverviewMetrics(
|
|
286
|
+
const overview = await dashStore.getOverviewMetrics('1h');
|
|
286
287
|
transmit.broadcast(dashChannel, overview);
|
|
287
288
|
}
|
|
288
289
|
catch {
|
package/dist/src/types.d.ts
CHANGED
|
@@ -281,10 +281,10 @@ export interface DevToolbarOptions {
|
|
|
281
281
|
*
|
|
282
282
|
* @example
|
|
283
283
|
* ```ts
|
|
284
|
-
* excludeFromTracing: ['/admin/api/debug']
|
|
284
|
+
* excludeFromTracing: ['/admin/api/debug', '/admin/api/server-stats']
|
|
285
285
|
* ```
|
|
286
286
|
*
|
|
287
|
-
* @default []
|
|
287
|
+
* @default ['/admin/api/debug', '/admin/api/server-stats']
|
|
288
288
|
*/
|
|
289
289
|
excludeFromTracing?: string[];
|
|
290
290
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared JSON parsing utilities with safe fallbacks.
|
|
3
|
+
*/
|
|
4
|
+
/** Safely parse a JSON string, returning the original value on failure. */
|
|
5
|
+
export declare function safeParseJson(value: any): any;
|
|
6
|
+
/** Safely parse a JSON string expected to be an array, returning [] on failure. */
|
|
7
|
+
export declare function safeParseJsonArray(value: any): any[];
|
|
8
|
+
//# sourceMappingURL=json_helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json_helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/json_helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG,CAQ7C;AAED,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG,EAAE,CAGpD"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared JSON parsing utilities with safe fallbacks.
|
|
3
|
+
*/
|
|
4
|
+
/** Safely parse a JSON string, returning the original value on failure. */
|
|
5
|
+
export function safeParseJson(value) {
|
|
6
|
+
if (value === null || value === undefined)
|
|
7
|
+
return null;
|
|
8
|
+
if (typeof value !== 'string')
|
|
9
|
+
return value;
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(value);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** Safely parse a JSON string expected to be an array, returning [] on failure. */
|
|
18
|
+
export function safeParseJsonArray(value) {
|
|
19
|
+
const parsed = safeParseJson(value);
|
|
20
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
21
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared email address extraction utilities.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Normalize various AdonisJS mail address formats to a comma-separated string.
|
|
6
|
+
*
|
|
7
|
+
* Handles:
|
|
8
|
+
* - A string: `"user@example.com"`
|
|
9
|
+
* - An object: `{ address: "user@example.com", name: "User" }`
|
|
10
|
+
* - An array of strings or objects
|
|
11
|
+
*/
|
|
12
|
+
export declare function extractAddresses(value: any): string;
|
|
13
|
+
//# sourceMappingURL=mail_helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mail_helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/mail_helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,CAWnD"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared email address extraction utilities.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Normalize various AdonisJS mail address formats to a comma-separated string.
|
|
6
|
+
*
|
|
7
|
+
* Handles:
|
|
8
|
+
* - A string: `"user@example.com"`
|
|
9
|
+
* - An object: `{ address: "user@example.com", name: "User" }`
|
|
10
|
+
* - An array of strings or objects
|
|
11
|
+
*/
|
|
12
|
+
export function extractAddresses(value) {
|
|
13
|
+
if (!value)
|
|
14
|
+
return '';
|
|
15
|
+
if (typeof value === 'string')
|
|
16
|
+
return value;
|
|
17
|
+
if (Array.isArray(value)) {
|
|
18
|
+
return value
|
|
19
|
+
.map((v) => (typeof v === 'string' ? v : v?.address || ''))
|
|
20
|
+
.filter(Boolean)
|
|
21
|
+
.join(', ');
|
|
22
|
+
}
|
|
23
|
+
if (typeof value === 'object' && value.address)
|
|
24
|
+
return value.address;
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared math utilities used across dashboard, collectors, and chart aggregator.
|
|
3
|
+
*/
|
|
4
|
+
/** Round a number to 2 decimal places. */
|
|
5
|
+
export declare function round(n: number): number;
|
|
6
|
+
/** Clamp a value between min and max. */
|
|
7
|
+
export declare function clamp(value: number, min: number, max: number): number;
|
|
8
|
+
//# sourceMappingURL=math_helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"math_helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/math_helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,0CAA0C;AAC1C,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEvC;AAED,yCAAyC;AACzC,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAErE"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared math utilities used across dashboard, collectors, and chart aggregator.
|
|
3
|
+
*/
|
|
4
|
+
/** Round a number to 2 decimal places. */
|
|
5
|
+
export function round(n) {
|
|
6
|
+
return Math.round(n * 100) / 100;
|
|
7
|
+
}
|
|
8
|
+
/** Clamp a value between min and max. */
|
|
9
|
+
export function clamp(value, min, max) {
|
|
10
|
+
return Math.max(min, Math.min(max, value));
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared time/range utilities for dashboard store, controller, and chart aggregator.
|
|
3
|
+
*/
|
|
4
|
+
/** Convert a range string (e.g. '1h', '7d') to total minutes. */
|
|
5
|
+
export declare function rangeToMinutes(range: string): number;
|
|
6
|
+
/** Convert a range string to a SQLite-compatible datetime cutoff. */
|
|
7
|
+
export declare function rangeToCutoff(range: string): string;
|
|
8
|
+
/** Convert a Date to a SQLite-compatible datetime string (YYYY-MM-DD HH:MM:SS). */
|
|
9
|
+
export declare function toSqliteTimestamp(date: Date): string;
|
|
10
|
+
/** Round a bucket timestamp string down to the nearest N minutes. */
|
|
11
|
+
export declare function roundBucket(bucket: string, minutes: number): string;
|
|
12
|
+
//# sourceMappingURL=time_helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time_helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/time_helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED,mFAAmF;AACnF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAEpD;AAED,qEAAqE;AACrE,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAKnE"}
|