@buenojs/bueno 0.8.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.
- package/.env.example +109 -0
- package/.github/workflows/ci.yml +31 -0
- package/LICENSE +21 -0
- package/README.md +892 -0
- package/architecture.md +652 -0
- package/bun.lock +70 -0
- package/dist/cli/index.js +3233 -0
- package/dist/index.js +9014 -0
- package/package.json +77 -0
- package/src/cache/index.ts +795 -0
- package/src/cli/ARCHITECTURE.md +837 -0
- package/src/cli/bin.ts +10 -0
- package/src/cli/commands/build.ts +425 -0
- package/src/cli/commands/dev.ts +248 -0
- package/src/cli/commands/generate.ts +541 -0
- package/src/cli/commands/help.ts +55 -0
- package/src/cli/commands/index.ts +112 -0
- package/src/cli/commands/migration.ts +355 -0
- package/src/cli/commands/new.ts +804 -0
- package/src/cli/commands/start.ts +208 -0
- package/src/cli/core/args.ts +283 -0
- package/src/cli/core/console.ts +349 -0
- package/src/cli/core/index.ts +60 -0
- package/src/cli/core/prompt.ts +424 -0
- package/src/cli/core/spinner.ts +265 -0
- package/src/cli/index.ts +135 -0
- package/src/cli/templates/deploy.ts +295 -0
- package/src/cli/templates/docker.ts +307 -0
- package/src/cli/templates/index.ts +24 -0
- package/src/cli/utils/fs.ts +428 -0
- package/src/cli/utils/index.ts +8 -0
- package/src/cli/utils/strings.ts +197 -0
- package/src/config/env.ts +408 -0
- package/src/config/index.ts +506 -0
- package/src/config/loader.ts +329 -0
- package/src/config/merge.ts +285 -0
- package/src/config/types.ts +320 -0
- package/src/config/validation.ts +441 -0
- package/src/container/forward-ref.ts +143 -0
- package/src/container/index.ts +386 -0
- package/src/context/index.ts +360 -0
- package/src/database/index.ts +1142 -0
- package/src/database/migrations/index.ts +371 -0
- package/src/database/schema/index.ts +619 -0
- package/src/frontend/api-routes.ts +640 -0
- package/src/frontend/bundler.ts +643 -0
- package/src/frontend/console-client.ts +419 -0
- package/src/frontend/console-stream.ts +587 -0
- package/src/frontend/dev-server.ts +846 -0
- package/src/frontend/file-router.ts +611 -0
- package/src/frontend/frameworks/index.ts +106 -0
- package/src/frontend/frameworks/react.ts +85 -0
- package/src/frontend/frameworks/solid.ts +104 -0
- package/src/frontend/frameworks/svelte.ts +110 -0
- package/src/frontend/frameworks/vue.ts +92 -0
- package/src/frontend/hmr-client.ts +663 -0
- package/src/frontend/hmr.ts +728 -0
- package/src/frontend/index.ts +342 -0
- package/src/frontend/islands.ts +552 -0
- package/src/frontend/isr.ts +555 -0
- package/src/frontend/layout.ts +475 -0
- package/src/frontend/ssr/react.ts +446 -0
- package/src/frontend/ssr/solid.ts +523 -0
- package/src/frontend/ssr/svelte.ts +546 -0
- package/src/frontend/ssr/vue.ts +504 -0
- package/src/frontend/ssr.ts +699 -0
- package/src/frontend/types.ts +2274 -0
- package/src/health/index.ts +604 -0
- package/src/index.ts +410 -0
- package/src/lock/index.ts +587 -0
- package/src/logger/index.ts +444 -0
- package/src/logger/transports/index.ts +969 -0
- package/src/metrics/index.ts +494 -0
- package/src/middleware/built-in.ts +360 -0
- package/src/middleware/index.ts +94 -0
- package/src/modules/filters.ts +458 -0
- package/src/modules/guards.ts +405 -0
- package/src/modules/index.ts +1256 -0
- package/src/modules/interceptors.ts +574 -0
- package/src/modules/lazy.ts +418 -0
- package/src/modules/lifecycle.ts +478 -0
- package/src/modules/metadata.ts +90 -0
- package/src/modules/pipes.ts +626 -0
- package/src/router/index.ts +339 -0
- package/src/router/linear.ts +371 -0
- package/src/router/regex.ts +292 -0
- package/src/router/tree.ts +562 -0
- package/src/rpc/index.ts +1263 -0
- package/src/security/index.ts +436 -0
- package/src/ssg/index.ts +631 -0
- package/src/storage/index.ts +456 -0
- package/src/telemetry/index.ts +1097 -0
- package/src/testing/index.ts +1586 -0
- package/src/types/index.ts +236 -0
- package/src/types/optional-deps.d.ts +219 -0
- package/src/validation/index.ts +276 -0
- package/src/websocket/index.ts +1004 -0
- package/tests/integration/cli.test.ts +1016 -0
- package/tests/integration/fullstack.test.ts +234 -0
- package/tests/unit/cache.test.ts +174 -0
- package/tests/unit/cli-commands.test.ts +892 -0
- package/tests/unit/cli.test.ts +1258 -0
- package/tests/unit/container.test.ts +279 -0
- package/tests/unit/context.test.ts +221 -0
- package/tests/unit/database.test.ts +183 -0
- package/tests/unit/linear-router.test.ts +280 -0
- package/tests/unit/lock.test.ts +336 -0
- package/tests/unit/middleware.test.ts +184 -0
- package/tests/unit/modules.test.ts +142 -0
- package/tests/unit/pubsub.test.ts +257 -0
- package/tests/unit/regex-router.test.ts +265 -0
- package/tests/unit/router.test.ts +373 -0
- package/tests/unit/rpc.test.ts +1248 -0
- package/tests/unit/security.test.ts +174 -0
- package/tests/unit/telemetry.test.ts +371 -0
- package/tests/unit/test-cache.test.ts +110 -0
- package/tests/unit/test-database.test.ts +282 -0
- package/tests/unit/tree-router.test.ts +325 -0
- package/tests/unit/validation.test.ts +794 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime Metrics Module
|
|
3
|
+
*
|
|
4
|
+
* Provides runtime metrics collection for memory, CPU usage, and other statistics.
|
|
5
|
+
* Part of Layer 7 (Testing & Observability) implementation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ============= Types =============
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Runtime metrics snapshot
|
|
12
|
+
*/
|
|
13
|
+
export interface RuntimeMetrics {
|
|
14
|
+
// Memory
|
|
15
|
+
/** Heap memory used in bytes */
|
|
16
|
+
memoryHeapUsed: number;
|
|
17
|
+
/** Total heap memory in bytes */
|
|
18
|
+
memoryHeapTotal: number;
|
|
19
|
+
/** External memory (C++ objects bound to JS) in bytes */
|
|
20
|
+
memoryExternal: number;
|
|
21
|
+
/** Resident Set Size in bytes */
|
|
22
|
+
memoryRss: number;
|
|
23
|
+
|
|
24
|
+
// CPU
|
|
25
|
+
/** User CPU time in microseconds */
|
|
26
|
+
cpuUser: number;
|
|
27
|
+
/** System CPU time in microseconds */
|
|
28
|
+
cpuSystem: number;
|
|
29
|
+
|
|
30
|
+
// Runtime
|
|
31
|
+
/** Process uptime in seconds */
|
|
32
|
+
uptime: number;
|
|
33
|
+
/** Event loop lag in milliseconds */
|
|
34
|
+
eventLoopLag: number;
|
|
35
|
+
|
|
36
|
+
// Timestamp
|
|
37
|
+
/** ISO 8601 timestamp */
|
|
38
|
+
timestamp: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Human-readable metrics summary
|
|
43
|
+
*/
|
|
44
|
+
export interface MetricsSummary {
|
|
45
|
+
memoryHeapUsed: string;
|
|
46
|
+
memoryHeapTotal: string;
|
|
47
|
+
memoryExternal: string;
|
|
48
|
+
memoryRss: string;
|
|
49
|
+
cpuUser: string;
|
|
50
|
+
cpuSystem: string;
|
|
51
|
+
uptime: string;
|
|
52
|
+
eventLoopLag: string;
|
|
53
|
+
timestamp: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Options for MetricsCollector
|
|
58
|
+
*/
|
|
59
|
+
export interface MetricsCollectorOptions {
|
|
60
|
+
/** Maximum number of historical snapshots to keep (default: 100) */
|
|
61
|
+
maxHistorySize?: number;
|
|
62
|
+
/** Include event loop lag measurement (default: true) */
|
|
63
|
+
measureEventLoopLag?: boolean;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Averaged metrics over time
|
|
68
|
+
*/
|
|
69
|
+
export interface AveragedMetrics {
|
|
70
|
+
avgMemoryHeapUsed: number;
|
|
71
|
+
avgMemoryHeapTotal: number;
|
|
72
|
+
avgMemoryExternal: number;
|
|
73
|
+
avgMemoryRss: number;
|
|
74
|
+
avgCpuUser: number;
|
|
75
|
+
avgCpuSystem: number;
|
|
76
|
+
avgEventLoopLag: number;
|
|
77
|
+
minMemoryHeapUsed: number;
|
|
78
|
+
maxMemoryHeapUsed: number;
|
|
79
|
+
sampleCount: number;
|
|
80
|
+
timeRange: {
|
|
81
|
+
start: string;
|
|
82
|
+
end: string;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ============= Helper Functions =============
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Format bytes to human-readable string
|
|
90
|
+
*/
|
|
91
|
+
export function formatBytes(bytes: number): string {
|
|
92
|
+
if (bytes === 0) return "0 B";
|
|
93
|
+
|
|
94
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
95
|
+
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
96
|
+
const value = bytes / Math.pow(1024, i);
|
|
97
|
+
|
|
98
|
+
// Use appropriate decimal places
|
|
99
|
+
if (i === 0) return `${value} ${units[i]}`;
|
|
100
|
+
if (value >= 100) return `${value.toFixed(1)} ${units[i]}`;
|
|
101
|
+
return `${value.toFixed(2)} ${units[i]}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Format microseconds to human-readable string
|
|
106
|
+
*/
|
|
107
|
+
export function formatMicroseconds(microseconds: number): string {
|
|
108
|
+
if (microseconds < 1000) {
|
|
109
|
+
return `${microseconds.toFixed(0)} µs`;
|
|
110
|
+
}
|
|
111
|
+
const ms = microseconds / 1000;
|
|
112
|
+
if (ms < 1000) {
|
|
113
|
+
return `${ms.toFixed(2)} ms`;
|
|
114
|
+
}
|
|
115
|
+
const seconds = ms / 1000;
|
|
116
|
+
return `${seconds.toFixed(2)} s`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Format seconds to human-readable uptime string
|
|
121
|
+
*/
|
|
122
|
+
export function formatUptime(seconds: number): string {
|
|
123
|
+
const days = Math.floor(seconds / 86400);
|
|
124
|
+
const hours = Math.floor((seconds % 86400) / 3600);
|
|
125
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
126
|
+
const secs = Math.floor(seconds % 60);
|
|
127
|
+
|
|
128
|
+
const parts: string[] = [];
|
|
129
|
+
if (days > 0) parts.push(`${days}d`);
|
|
130
|
+
if (hours > 0) parts.push(`${hours}h`);
|
|
131
|
+
if (minutes > 0) parts.push(`${minutes}m`);
|
|
132
|
+
if (secs > 0 || parts.length === 0) parts.push(`${secs}s`);
|
|
133
|
+
|
|
134
|
+
return parts.join(" ");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get human-readable metrics summary
|
|
139
|
+
*/
|
|
140
|
+
export function getMetricsSummary(metrics: RuntimeMetrics): MetricsSummary {
|
|
141
|
+
return {
|
|
142
|
+
memoryHeapUsed: formatBytes(metrics.memoryHeapUsed),
|
|
143
|
+
memoryHeapTotal: formatBytes(metrics.memoryHeapTotal),
|
|
144
|
+
memoryExternal: formatBytes(metrics.memoryExternal),
|
|
145
|
+
memoryRss: formatBytes(metrics.memoryRss),
|
|
146
|
+
cpuUser: formatMicroseconds(metrics.cpuUser),
|
|
147
|
+
cpuSystem: formatMicroseconds(metrics.cpuSystem),
|
|
148
|
+
uptime: formatUptime(metrics.uptime),
|
|
149
|
+
eventLoopLag: `${metrics.eventLoopLag.toFixed(2)} ms`,
|
|
150
|
+
timestamp: metrics.timestamp,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ============= Event Loop Lag Measurement =============
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Measure event loop lag using deferred execution timing
|
|
158
|
+
* @returns Promise resolving to lag in milliseconds
|
|
159
|
+
*/
|
|
160
|
+
export async function measureEventLoopLag(): Promise<number> {
|
|
161
|
+
const start = Bun.nanoseconds();
|
|
162
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
163
|
+
const end = Bun.nanoseconds();
|
|
164
|
+
return (end - start) / 1e6; // Convert nanoseconds to milliseconds
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Measure event loop lag multiple times and return average
|
|
169
|
+
*/
|
|
170
|
+
export async function measureEventLoopLagAverage(
|
|
171
|
+
samples: number = 5,
|
|
172
|
+
): Promise<number> {
|
|
173
|
+
const measurements: number[] = [];
|
|
174
|
+
|
|
175
|
+
for (let i = 0; i < samples; i++) {
|
|
176
|
+
measurements.push(await measureEventLoopLag());
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return measurements.reduce((a, b) => a + b, 0) / measurements.length;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ============= MetricsCollector Class =============
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Collects and manages runtime metrics
|
|
186
|
+
*/
|
|
187
|
+
export class MetricsCollector {
|
|
188
|
+
private history: RuntimeMetrics[] = [];
|
|
189
|
+
private maxHistorySize: number;
|
|
190
|
+
private measureEventLoopLagEnabled: boolean;
|
|
191
|
+
private periodicTimer: Timer | null = null;
|
|
192
|
+
private lastCpuUsage: NodeJS.CpuUsage | undefined = undefined;
|
|
193
|
+
|
|
194
|
+
constructor(options: MetricsCollectorOptions = {}) {
|
|
195
|
+
this.maxHistorySize = options.maxHistorySize ?? 100;
|
|
196
|
+
this.measureEventLoopLagEnabled = options.measureEventLoopLag ?? true;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Collect current runtime metrics
|
|
201
|
+
*/
|
|
202
|
+
async collect(): Promise<RuntimeMetrics> {
|
|
203
|
+
// Get memory metrics
|
|
204
|
+
const memUsage = process.memoryUsage();
|
|
205
|
+
|
|
206
|
+
// Get CPU metrics
|
|
207
|
+
const cpuUsage = process.cpuUsage(this.lastCpuUsage);
|
|
208
|
+
this.lastCpuUsage = process.cpuUsage();
|
|
209
|
+
|
|
210
|
+
// Get uptime
|
|
211
|
+
const uptime = process.uptime();
|
|
212
|
+
|
|
213
|
+
// Measure event loop lag if enabled
|
|
214
|
+
let eventLoopLag = 0;
|
|
215
|
+
if (this.measureEventLoopLagEnabled) {
|
|
216
|
+
eventLoopLag = await measureEventLoopLag();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const metrics: RuntimeMetrics = {
|
|
220
|
+
memoryHeapUsed: memUsage.heapUsed,
|
|
221
|
+
memoryHeapTotal: memUsage.heapTotal,
|
|
222
|
+
memoryExternal: memUsage.external,
|
|
223
|
+
memoryRss: memUsage.rss,
|
|
224
|
+
cpuUser: cpuUsage.user,
|
|
225
|
+
cpuSystem: cpuUsage.system,
|
|
226
|
+
uptime,
|
|
227
|
+
eventLoopLag,
|
|
228
|
+
timestamp: new Date().toISOString(),
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// Add to history
|
|
232
|
+
this.history.push(metrics);
|
|
233
|
+
|
|
234
|
+
// Trim history if needed
|
|
235
|
+
if (this.history.length > this.maxHistorySize) {
|
|
236
|
+
this.history.shift();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return metrics;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Start collecting metrics at regular intervals
|
|
244
|
+
* @param intervalMs Interval in milliseconds (default: 5000)
|
|
245
|
+
*/
|
|
246
|
+
startPeriodicCollection(intervalMs: number = 5000): void {
|
|
247
|
+
if (this.periodicTimer !== null) {
|
|
248
|
+
throw new Error("Periodic collection is already running");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Collect immediately
|
|
252
|
+
this.collect().catch(() => {
|
|
253
|
+
// Ignore errors in initial collection
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Set up periodic collection
|
|
257
|
+
this.periodicTimer = setInterval(() => {
|
|
258
|
+
this.collect().catch(() => {
|
|
259
|
+
// Ignore errors in periodic collection
|
|
260
|
+
});
|
|
261
|
+
}, intervalMs);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Stop periodic collection
|
|
266
|
+
*/
|
|
267
|
+
stopPeriodicCollection(): void {
|
|
268
|
+
if (this.periodicTimer !== null) {
|
|
269
|
+
clearInterval(this.periodicTimer);
|
|
270
|
+
this.periodicTimer = null;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Check if periodic collection is running
|
|
276
|
+
*/
|
|
277
|
+
isCollecting(): boolean {
|
|
278
|
+
return this.periodicTimer !== null;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Get historical metrics snapshots
|
|
283
|
+
*/
|
|
284
|
+
getHistory(): RuntimeMetrics[] {
|
|
285
|
+
return [...this.history];
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Get the most recent metrics
|
|
290
|
+
*/
|
|
291
|
+
getLatest(): RuntimeMetrics | null {
|
|
292
|
+
return this.history.length > 0
|
|
293
|
+
? { ...this.history[this.history.length - 1] }
|
|
294
|
+
: null;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Get averaged metrics over time
|
|
299
|
+
*/
|
|
300
|
+
getAverage(): AveragedMetrics | null {
|
|
301
|
+
if (this.history.length === 0) {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const count = this.history.length;
|
|
306
|
+
let sumHeapUsed = 0;
|
|
307
|
+
let sumHeapTotal = 0;
|
|
308
|
+
let sumExternal = 0;
|
|
309
|
+
let sumRss = 0;
|
|
310
|
+
let sumCpuUser = 0;
|
|
311
|
+
let sumCpuSystem = 0;
|
|
312
|
+
let sumEventLoopLag = 0;
|
|
313
|
+
let minHeapUsed = Infinity;
|
|
314
|
+
let maxHeapUsed = 0;
|
|
315
|
+
|
|
316
|
+
for (const m of this.history) {
|
|
317
|
+
sumHeapUsed += m.memoryHeapUsed;
|
|
318
|
+
sumHeapTotal += m.memoryHeapTotal;
|
|
319
|
+
sumExternal += m.memoryExternal;
|
|
320
|
+
sumRss += m.memoryRss;
|
|
321
|
+
sumCpuUser += m.cpuUser;
|
|
322
|
+
sumCpuSystem += m.cpuSystem;
|
|
323
|
+
sumEventLoopLag += m.eventLoopLag;
|
|
324
|
+
minHeapUsed = Math.min(minHeapUsed, m.memoryHeapUsed);
|
|
325
|
+
maxHeapUsed = Math.max(maxHeapUsed, m.memoryHeapUsed);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return {
|
|
329
|
+
avgMemoryHeapUsed: Math.round(sumHeapUsed / count),
|
|
330
|
+
avgMemoryHeapTotal: Math.round(sumHeapTotal / count),
|
|
331
|
+
avgMemoryExternal: Math.round(sumExternal / count),
|
|
332
|
+
avgMemoryRss: Math.round(sumRss / count),
|
|
333
|
+
avgCpuUser: Math.round(sumCpuUser / count),
|
|
334
|
+
avgCpuSystem: Math.round(sumCpuSystem / count),
|
|
335
|
+
avgEventLoopLag: Math.round((sumEventLoopLag / count) * 100) / 100,
|
|
336
|
+
minMemoryHeapUsed: minHeapUsed === Infinity ? 0 : minHeapUsed,
|
|
337
|
+
maxMemoryHeapUsed: maxHeapUsed,
|
|
338
|
+
sampleCount: count,
|
|
339
|
+
timeRange: {
|
|
340
|
+
start: this.history[0].timestamp,
|
|
341
|
+
end: this.history[count - 1].timestamp,
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Clear history
|
|
348
|
+
*/
|
|
349
|
+
reset(): void {
|
|
350
|
+
this.history = [];
|
|
351
|
+
this.lastCpuUsage = undefined;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Get the number of samples in history
|
|
356
|
+
*/
|
|
357
|
+
getHistorySize(): number {
|
|
358
|
+
return this.history.length;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ============= Prometheus Format Export =============
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Export metrics in Prometheus text format
|
|
366
|
+
* @param metrics Runtime metrics to export
|
|
367
|
+
* @returns Prometheus formatted string
|
|
368
|
+
*/
|
|
369
|
+
export function toPrometheusFormat(metrics: RuntimeMetrics): string {
|
|
370
|
+
const timestamp = Date.now();
|
|
371
|
+
const lines: string[] = [];
|
|
372
|
+
|
|
373
|
+
// Memory metrics
|
|
374
|
+
lines.push("# HELP process_memory_heap_used_bytes Heap memory used in bytes");
|
|
375
|
+
lines.push("# TYPE process_memory_heap_used_bytes gauge");
|
|
376
|
+
lines.push(
|
|
377
|
+
`process_memory_heap_used_bytes ${metrics.memoryHeapUsed} ${timestamp}`,
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
lines.push("# HELP process_memory_heap_total_bytes Total heap memory in bytes");
|
|
381
|
+
lines.push("# TYPE process_memory_heap_total_bytes gauge");
|
|
382
|
+
lines.push(
|
|
383
|
+
`process_memory_heap_total_bytes ${metrics.memoryHeapTotal} ${timestamp}`,
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
lines.push("# HELP process_memory_external_bytes External memory in bytes");
|
|
387
|
+
lines.push("# TYPE process_memory_external_bytes gauge");
|
|
388
|
+
lines.push(
|
|
389
|
+
`process_memory_external_bytes ${metrics.memoryExternal} ${timestamp}`,
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
lines.push("# HELP process_memory_rss_bytes Resident Set Size in bytes");
|
|
393
|
+
lines.push("# TYPE process_memory_rss_bytes gauge");
|
|
394
|
+
lines.push(`process_memory_rss_bytes ${metrics.memoryRss} ${timestamp}`);
|
|
395
|
+
|
|
396
|
+
// CPU metrics
|
|
397
|
+
lines.push("# HELP process_cpu_user_seconds_total Total user CPU time");
|
|
398
|
+
lines.push("# TYPE process_cpu_user_seconds_total counter");
|
|
399
|
+
lines.push(
|
|
400
|
+
`process_cpu_user_seconds_total ${(metrics.cpuUser / 1e6).toFixed(6)} ${timestamp}`,
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
lines.push("# HELP process_cpu_system_seconds_total Total system CPU time");
|
|
404
|
+
lines.push("# TYPE process_cpu_system_seconds_total counter");
|
|
405
|
+
lines.push(
|
|
406
|
+
`process_cpu_system_seconds_total ${(metrics.cpuSystem / 1e6).toFixed(6)} ${timestamp}`,
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
// Runtime metrics
|
|
410
|
+
lines.push("# HELP process_uptime_seconds Process uptime in seconds");
|
|
411
|
+
lines.push("# TYPE process_uptime_seconds gauge");
|
|
412
|
+
lines.push(`process_uptime_seconds ${metrics.uptime.toFixed(2)} ${timestamp}`);
|
|
413
|
+
|
|
414
|
+
lines.push("# HELP nodejs_eventloop_lag_ms Event loop lag in milliseconds");
|
|
415
|
+
lines.push("# TYPE nodejs_eventloop_lag_ms gauge");
|
|
416
|
+
lines.push(
|
|
417
|
+
`nodejs_eventloop_lag_ms ${metrics.eventLoopLag.toFixed(2)} ${timestamp}`,
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
return lines.join("\n");
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Export averaged metrics in Prometheus format
|
|
425
|
+
*/
|
|
426
|
+
export function averagedMetricsToPrometheus(
|
|
427
|
+
averaged: AveragedMetrics,
|
|
428
|
+
): string {
|
|
429
|
+
const timestamp = Date.now();
|
|
430
|
+
const lines: string[] = [];
|
|
431
|
+
|
|
432
|
+
lines.push("# HELP process_memory_heap_used_avg_bytes Average heap memory used");
|
|
433
|
+
lines.push("# TYPE process_memory_heap_used_avg_bytes gauge");
|
|
434
|
+
lines.push(
|
|
435
|
+
`process_memory_heap_used_avg_bytes ${averaged.avgMemoryHeapUsed} ${timestamp}`,
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
lines.push("# HELP process_memory_heap_used_min_bytes Minimum heap memory used");
|
|
439
|
+
lines.push("# TYPE process_memory_heap_used_min_bytes gauge");
|
|
440
|
+
lines.push(
|
|
441
|
+
`process_memory_heap_used_min_bytes ${averaged.minMemoryHeapUsed} ${timestamp}`,
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
lines.push("# HELP process_memory_heap_used_max_bytes Maximum heap memory used");
|
|
445
|
+
lines.push("# TYPE process_memory_heap_used_max_bytes gauge");
|
|
446
|
+
lines.push(
|
|
447
|
+
`process_memory_heap_used_max_bytes ${averaged.maxMemoryHeapUsed} ${timestamp}`,
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
lines.push("# HELP process_eventloop_lag_avg_ms Average event loop lag");
|
|
451
|
+
lines.push("# TYPE process_eventloop_lag_avg_ms gauge");
|
|
452
|
+
lines.push(
|
|
453
|
+
`process_eventloop_lag_avg_ms ${averaged.avgEventLoopLag.toFixed(2)} ${timestamp}`,
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
lines.push("# HELP process_metrics_sample_count Number of samples collected");
|
|
457
|
+
lines.push("# TYPE process_metrics_sample_count gauge");
|
|
458
|
+
lines.push(`process_metrics_sample_count ${averaged.sampleCount} ${timestamp}`);
|
|
459
|
+
|
|
460
|
+
return lines.join("\n");
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// ============= Factory Functions =============
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Create a new metrics collector
|
|
467
|
+
*/
|
|
468
|
+
export function createMetricsCollector(
|
|
469
|
+
options?: MetricsCollectorOptions,
|
|
470
|
+
): MetricsCollector {
|
|
471
|
+
return new MetricsCollector(options);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Collect metrics once without a collector
|
|
476
|
+
*/
|
|
477
|
+
export async function collectMetrics(): Promise<RuntimeMetrics> {
|
|
478
|
+
const memUsage = process.memoryUsage();
|
|
479
|
+
const cpuUsage = process.cpuUsage();
|
|
480
|
+
const uptime = process.uptime();
|
|
481
|
+
const eventLoopLag = await measureEventLoopLag();
|
|
482
|
+
|
|
483
|
+
return {
|
|
484
|
+
memoryHeapUsed: memUsage.heapUsed,
|
|
485
|
+
memoryHeapTotal: memUsage.heapTotal,
|
|
486
|
+
memoryExternal: memUsage.external,
|
|
487
|
+
memoryRss: memUsage.rss,
|
|
488
|
+
cpuUser: cpuUsage.user,
|
|
489
|
+
cpuSystem: cpuUsage.system,
|
|
490
|
+
uptime,
|
|
491
|
+
eventLoopLag,
|
|
492
|
+
timestamp: new Date().toISOString(),
|
|
493
|
+
};
|
|
494
|
+
}
|