@lakutata/monitor 3.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +114 -0
  3. package/dist/cjs/exports/Monitor.d.ts +9 -0
  4. package/dist/cjs/exports/Monitor.js +28 -0
  5. package/dist/cjs/monitor/CpuMonitor.d.ts +60 -0
  6. package/dist/cjs/monitor/CpuMonitor.js +96 -0
  7. package/dist/cjs/monitor/EventLoopMonitor.d.ts +65 -0
  8. package/dist/cjs/monitor/EventLoopMonitor.js +92 -0
  9. package/dist/cjs/monitor/HttpRequestMonitor.d.ts +61 -0
  10. package/dist/cjs/monitor/HttpRequestMonitor.js +92 -0
  11. package/dist/cjs/monitor/MemoryMonitor.d.ts +61 -0
  12. package/dist/cjs/monitor/MemoryMonitor.js +152 -0
  13. package/dist/cjs/monitor/interfaces/ICpuMonitorStatistics.d.ts +40 -0
  14. package/dist/cjs/monitor/interfaces/ICpuMonitorStatistics.js +2 -0
  15. package/dist/cjs/monitor/interfaces/IEventLoopMonitorStatistics.d.ts +45 -0
  16. package/dist/cjs/monitor/interfaces/IEventLoopMonitorStatistics.js +2 -0
  17. package/dist/cjs/monitor/interfaces/IHttpRequestMonitorStatistics.d.ts +43 -0
  18. package/dist/cjs/monitor/interfaces/IHttpRequestMonitorStatistics.js +2 -0
  19. package/dist/cjs/monitor/interfaces/IMemoryMonitorStatistics.d.ts +124 -0
  20. package/dist/cjs/monitor/interfaces/IMemoryMonitorStatistics.js +2 -0
  21. package/dist/cjs/monitor/interfaces/IMonitor.d.ts +10 -0
  22. package/dist/cjs/monitor/interfaces/IMonitor.js +2 -0
  23. package/dist/cjs/monitor/lib/Samples.d.ts +57 -0
  24. package/dist/cjs/monitor/lib/Samples.js +119 -0
  25. package/dist/cjs/package.json +1 -0
  26. package/dist/esm/exports/Monitor.js +9 -0
  27. package/dist/esm/monitor/CpuMonitor.js +92 -0
  28. package/dist/esm/monitor/EventLoopMonitor.js +88 -0
  29. package/dist/esm/monitor/HttpRequestMonitor.js +88 -0
  30. package/dist/esm/monitor/MemoryMonitor.js +115 -0
  31. package/dist/esm/monitor/interfaces/ICpuMonitorStatistics.js +1 -0
  32. package/dist/esm/monitor/interfaces/IEventLoopMonitorStatistics.js +1 -0
  33. package/dist/esm/monitor/interfaces/IHttpRequestMonitorStatistics.js +1 -0
  34. package/dist/esm/monitor/interfaces/IMemoryMonitorStatistics.js +1 -0
  35. package/dist/esm/monitor/interfaces/IMonitor.js +1 -0
  36. package/dist/esm/monitor/lib/Samples.js +114 -0
  37. package/dist/types/exports/Monitor.d.ts +9 -0
  38. package/dist/types/monitor/CpuMonitor.d.ts +60 -0
  39. package/dist/types/monitor/EventLoopMonitor.d.ts +65 -0
  40. package/dist/types/monitor/HttpRequestMonitor.d.ts +61 -0
  41. package/dist/types/monitor/MemoryMonitor.d.ts +61 -0
  42. package/dist/types/monitor/interfaces/ICpuMonitorStatistics.d.ts +40 -0
  43. package/dist/types/monitor/interfaces/IEventLoopMonitorStatistics.d.ts +45 -0
  44. package/dist/types/monitor/interfaces/IHttpRequestMonitorStatistics.d.ts +43 -0
  45. package/dist/types/monitor/interfaces/IMemoryMonitorStatistics.d.ts +124 -0
  46. package/dist/types/monitor/interfaces/IMonitor.d.ts +10 -0
  47. package/dist/types/monitor/lib/Samples.d.ts +57 -0
  48. package/package.json +44 -0
@@ -0,0 +1,61 @@
1
+ import { PerformanceObserver } from 'node:perf_hooks';
2
+ import { Component } from '@lakutata/core';
3
+ import { IMonitor } from './interfaces/IMonitor.js';
4
+ import { IHttpRequestMonitorStatistics } from './interfaces/IHttpRequestMonitorStatistics.js';
5
+ /**
6
+ * The HTTP request monitor: measures how long the HTTP requests received by the process take, in milliseconds, from
7
+ * their arrival to the end of their response, through the performance entries of `node:http` (so every server built on
8
+ * it is counted: the Express and Fastify adapters, and any other server of the process). The requests the process
9
+ * sends are not counted, nor are HTTP/2 requests.
10
+ *
11
+ * It keeps the count and the statistics of the durations since it was created or last {@link HttpRequestMonitor.reset},
12
+ * in constant memory. The entries reach it in batches, shortly after the responses end, so a request just answered may
13
+ * not be counted yet. Declare it in the `components` of the application (`{class: HttpRequestMonitor}`) and get it with
14
+ * `@Inject` or `getObject()`; it is a singleton. It only sees the requests ending after its creation: list it in
15
+ * `bootstrap` to measure from the launch; it stops when the component is destroyed.
16
+ * @example
17
+ * ```typescript
18
+ * import {Application} from 'lakutata'
19
+ * import {HttpRequestMonitor, type IHttpRequestMonitorStatistics} from 'lakutata/com/monitor'
20
+ *
21
+ * Application
22
+ * .run(() => ({
23
+ * id: 'http.app',
24
+ * name: 'Http',
25
+ * components: {
26
+ * httpRequests: {class: HttpRequestMonitor}
27
+ * },
28
+ * bootstrap: ['httpRequests']
29
+ * }))
30
+ * .onLaunched(async (app: Application): Promise<void> => {
31
+ * const httpRequests: HttpRequestMonitor = await app.getObject('httpRequests')
32
+ * setInterval((): void => {
33
+ * const statistics: IHttpRequestMonitorStatistics = httpRequests.statistics
34
+ * console.log(`${statistics.count} requests, avg ${statistics.avg} ms, p99 ${statistics.p99} ms`)
35
+ * httpRequests.reset()
36
+ * }, 60000)
37
+ * })
38
+ * ```
39
+ */
40
+ export declare class HttpRequestMonitor extends Component implements IMonitor<IHttpRequestMonitorStatistics> {
41
+ #private;
42
+ /**
43
+ * The observer of the `http` performance entries, recording the duration of each received request (the
44
+ * `HttpRequest` entries); observing from the monitor's initialization to its destruction.
45
+ */
46
+ protected readonly observer: PerformanceObserver;
47
+ /**
48
+ * The statistics of the request durations since the monitor was created or reset, computed at each read: the
49
+ * number of requests, and the minimum, maximum, mean, standard deviation and percentiles of their durations in
50
+ * milliseconds, rounded to 2 decimals. The percentiles are approximate (3 significant digits). All 0 before the first
51
+ * request.
52
+ */
53
+ get statistics(): IHttpRequestMonitorStatistics;
54
+ protected init(): Promise<void>;
55
+ protected destroy(): Promise<void>;
56
+ /**
57
+ * Forget the requests: the count and the statistics restart from 0. Use it to measure a period, such as between
58
+ * two reports.
59
+ */
60
+ reset(): void;
61
+ }
@@ -0,0 +1,61 @@
1
+ import { Component } from '@lakutata/core';
2
+ import { IMonitor } from './interfaces/IMonitor.js';
3
+ import { IMemoryMonitorStatistics } from './interfaces/IMemoryMonitorStatistics.js';
4
+ /**
5
+ * The memory monitor: measures the memory of the process, in bytes, with `process.memoryUsage()`: its resident set
6
+ * (the physical memory it occupies, `rss`), its V8 heap (`heapTotal` reserved, `heapUsed` occupied by the JavaScript
7
+ * objects) and its external memory (the C++ objects bound to JavaScript ones, the `Buffer`s included). It takes a
8
+ * sample when it is created then every second, and keeps the statistics of every sample since it was created or last
9
+ * {@link MemoryMonitor.reset}: the minimum, the maximum, the mean and percentiles, in constant memory.
10
+ *
11
+ * Declare it in the `components` of the application (`{class: MemoryMonitor}`) and get it with `@Inject` or
12
+ * `getObject()`; it is a singleton. It starts sampling when it is created: list it in `bootstrap` to measure from the
13
+ * launch. Its timer does not keep the process alive, and stops when the component is destroyed.
14
+ * @example
15
+ * ```typescript
16
+ * import {Application} from 'lakutata'
17
+ * import {MemoryMonitor, type IMemoryMonitorStatistics} from 'lakutata/com/monitor'
18
+ *
19
+ * Application
20
+ * .run(() => ({
21
+ * id: 'memory.app',
22
+ * name: 'Memory',
23
+ * components: {
24
+ * memory: {class: MemoryMonitor}
25
+ * },
26
+ * bootstrap: ['memory']
27
+ * }))
28
+ * .onLaunched(async (app: Application): Promise<void> => {
29
+ * const memory: MemoryMonitor = await app.getObject('memory')
30
+ * setInterval((): void => {
31
+ * const statistics: IMemoryMonitorStatistics = memory.statistics
32
+ * const megabytes = (bytes: number): string => (bytes / 1048576).toFixed(1)
33
+ * console.log(`RSS ${megabytes(statistics.physicalUsed)} MB (${statistics.physicalUsage}% of the host), heap ${statistics.heapUsage}% used`)
34
+ * }, 60000)
35
+ * })
36
+ * ```
37
+ */
38
+ export declare class MemoryMonitor extends Component implements IMonitor<IMemoryMonitorStatistics> {
39
+ #private;
40
+ /**
41
+ * The memory statistics, computed at each read: the latest sample (`physicalUsed`, `heapTotal`, `heapUsed`,
42
+ * `externalUsed`, in bytes, and the percentages `physicalUsage` and `heapUsage`, rounded to 2 decimals), the host's
43
+ * total memory, and the minimum, maximum, mean (rounded to 2 decimals) and percentiles of the samples since the
44
+ * monitor was created or reset. The percentiles are approximate (3 significant digits). The statistics of the
45
+ * samples are 0 after a reset until the next sample.
46
+ */
47
+ get statistics(): IMemoryMonitorStatistics;
48
+ /**
49
+ * Take a sample of the memory of the process: it becomes the latest usage and is added to the statistics. Called
50
+ * when the monitor is created, then every second.
51
+ * @protected
52
+ */
53
+ protected sampleMemoryUsage(): void;
54
+ protected init(): Promise<void>;
55
+ protected destroy(): Promise<void>;
56
+ /**
57
+ * Forget the samples: the minimum, maximum, mean and percentiles are 0 until the next sample, then cover the samples
58
+ * taken since. The latest usage is kept.
59
+ */
60
+ reset(): void;
61
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The statistics of a {@link CpuMonitor}: the CPU usage of the process in percent of one core (100 is one core fully
3
+ * busy; above 100 with several cores busy), each sample the average over one second, rounded to 5 decimals. The
4
+ * minimum, maximum, mean and percentiles cover the samples since the monitor was created or reset; the percentiles are
5
+ * approximate (3 significant digits). All 0 before the first sample.
6
+ */
7
+ export interface ICpuMonitorStatistics {
8
+ /**
9
+ * The CPU usage over the latest second (the latest sample), in percent of one core.
10
+ */
11
+ usage: number;
12
+ /**
13
+ * The lowest sample, in percent of one core.
14
+ */
15
+ usageMin: number;
16
+ /**
17
+ * The highest sample, in percent of one core.
18
+ */
19
+ usageMax: number;
20
+ /**
21
+ * The mean of the samples, in percent of one core.
22
+ */
23
+ usageAvg: number;
24
+ /**
25
+ * The median sample (50% of the seconds used less), in percent of one core.
26
+ */
27
+ usageP50: number;
28
+ /**
29
+ * The 90th percentile of the samples (90% of the seconds used less), in percent of one core.
30
+ */
31
+ usageP90: number;
32
+ /**
33
+ * The 95th percentile of the samples, in percent of one core.
34
+ */
35
+ usageP95: number;
36
+ /**
37
+ * The 99th percentile of the samples, in percent of one core.
38
+ */
39
+ usageP99: number;
40
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The statistics of an {@link EventLoopMonitor} since it was created or reset: the event loop delays, in milliseconds
3
+ * (measured by a timer due every millisecond: about 1 ms when the loop is idle, more while synchronous work blocks
4
+ * it; all 0 before the first measure), and the loop utilization, in percent. The percentiles
5
+ * come from Node's histogram.
6
+ */
7
+ export interface IEventLoopMonitorStatistics {
8
+ /**
9
+ * The shortest delay, in milliseconds.
10
+ */
11
+ min: number;
12
+ /**
13
+ * The longest delay, in milliseconds: the longest the loop was blocked.
14
+ */
15
+ max: number;
16
+ /**
17
+ * The mean delay, in milliseconds.
18
+ */
19
+ avg: number;
20
+ /**
21
+ * The standard deviation of the delays, in milliseconds.
22
+ */
23
+ stdDev: number;
24
+ /**
25
+ * The median delay, in milliseconds.
26
+ */
27
+ p50: number;
28
+ /**
29
+ * The 90th percentile of the delays (90% were shorter), in milliseconds.
30
+ */
31
+ p90: number;
32
+ /**
33
+ * The 95th percentile of the delays, in milliseconds.
34
+ */
35
+ p95: number;
36
+ /**
37
+ * The 99th percentile of the delays, in milliseconds: the usual pick for an alert.
38
+ */
39
+ p99: number;
40
+ /**
41
+ * The share of the time the event loop was busy running code rather than waiting for I/O, in percent (0 to
42
+ * 100), since the monitor was created or reset. Not rounded.
43
+ */
44
+ utilRate: number;
45
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The statistics of an {@link HttpRequestMonitor} since it was created or reset: the number of HTTP requests received
3
+ * and their durations, from their arrival to the end of their response, in milliseconds rounded to 2 decimals. The
4
+ * percentiles are approximate (3 significant digits). All 0 before the first request.
5
+ */
6
+ export interface IHttpRequestMonitorStatistics {
7
+ /**
8
+ * The number of requests received whose response ended.
9
+ */
10
+ count: number;
11
+ /**
12
+ * The shortest request, in milliseconds.
13
+ */
14
+ min: number;
15
+ /**
16
+ * The longest request, in milliseconds.
17
+ */
18
+ max: number;
19
+ /**
20
+ * The mean duration, in milliseconds.
21
+ */
22
+ avg: number;
23
+ /**
24
+ * The standard deviation of the durations, in milliseconds.
25
+ */
26
+ stdDev: number;
27
+ /**
28
+ * The median duration, in milliseconds.
29
+ */
30
+ p50: number;
31
+ /**
32
+ * The 90th percentile of the durations (90% of the requests were faster), in milliseconds.
33
+ */
34
+ p90: number;
35
+ /**
36
+ * The 95th percentile of the durations, in milliseconds.
37
+ */
38
+ p95: number;
39
+ /**
40
+ * The 99th percentile of the durations, in milliseconds.
41
+ */
42
+ p99: number;
43
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * The statistics of a {@link MemoryMonitor}, in bytes unless stated: the latest sample of the process memory
3
+ * (`process.memoryUsage()`), and the minimum, maximum, mean and percentiles of the samples taken every second since
4
+ * the monitor was created or reset (0 after a reset until the next sample). The percentiles are approximate (3
5
+ * significant digits).
6
+ */
7
+ export interface IMemoryMonitorStatistics {
8
+ /**
9
+ * The total physical memory of the host (`os.totalmem()`), in bytes; under a container, the host's,
10
+ * not the container's limit.
11
+ */
12
+ physicalTotal: number;
13
+ /**
14
+ * The resident set size of the process (`rss`) at the latest sample: the physical memory it occupies, in
15
+ * bytes.
16
+ */
17
+ physicalUsed: number;
18
+ /**
19
+ * `physicalUsed` as a percentage of `physicalTotal`, rounded to 2 decimals.
20
+ */
21
+ physicalUsage: number;
22
+ /**
23
+ * The lowest resident set size sampled, in bytes.
24
+ */
25
+ physicalUsedMin: number;
26
+ /**
27
+ * The highest resident set size sampled, in bytes.
28
+ */
29
+ physicalUsedMax: number;
30
+ /**
31
+ * The mean resident set size of the samples, in bytes, rounded to 2 decimals.
32
+ */
33
+ physicalUsedAvg: number;
34
+ /**
35
+ * The median resident set size of the samples, in bytes.
36
+ */
37
+ physicalUsedP50: number;
38
+ /**
39
+ * The 90th percentile of the resident set size samples, in bytes.
40
+ */
41
+ physicalUsedP90: number;
42
+ /**
43
+ * The 95th percentile of the resident set size samples, in bytes.
44
+ */
45
+ physicalUsedP95: number;
46
+ /**
47
+ * The 99th percentile of the resident set size samples, in bytes.
48
+ */
49
+ physicalUsedP99: number;
50
+ /**
51
+ * The size of the V8 heap reserved by the process at the latest sample, in bytes.
52
+ */
53
+ heapTotal: number;
54
+ /**
55
+ * The part of the V8 heap occupied by JavaScript objects at the latest sample, in bytes.
56
+ */
57
+ heapUsed: number;
58
+ /**
59
+ * `heapUsed` as a percentage of `heapTotal`, rounded to 2 decimals. V8 grows the heap as needed, so a
60
+ * high value alone is not a leak: a leak shows as `heapUsedMin` rising from one reset period to the next.
61
+ */
62
+ heapUsage: number;
63
+ /**
64
+ * The lowest used heap sampled, in bytes.
65
+ */
66
+ heapUsedMin: number;
67
+ /**
68
+ * The highest used heap sampled, in bytes.
69
+ */
70
+ heapUsedMax: number;
71
+ /**
72
+ * The mean used heap of the samples, in bytes, rounded to 2 decimals.
73
+ */
74
+ heapUsedAvg: number;
75
+ /**
76
+ * The median used heap of the samples, in bytes.
77
+ */
78
+ heapUsedP50: number;
79
+ /**
80
+ * The 90th percentile of the used heap samples, in bytes.
81
+ */
82
+ heapUsedP90: number;
83
+ /**
84
+ * The 95th percentile of the used heap samples, in bytes.
85
+ */
86
+ heapUsedP95: number;
87
+ /**
88
+ * The 99th percentile of the used heap samples, in bytes.
89
+ */
90
+ heapUsedP99: number;
91
+ /**
92
+ * The memory of the C++ objects bound to JavaScript objects (the `Buffer`s included) at the latest
93
+ * sample, in bytes.
94
+ */
95
+ externalUsed: number;
96
+ /**
97
+ * The lowest external memory sampled, in bytes.
98
+ */
99
+ externalUsedMin: number;
100
+ /**
101
+ * The highest external memory sampled, in bytes.
102
+ */
103
+ externalUsedMax: number;
104
+ /**
105
+ * The mean external memory of the samples, in bytes, rounded to 2 decimals.
106
+ */
107
+ externalUsedAvg: number;
108
+ /**
109
+ * The median external memory of the samples, in bytes.
110
+ */
111
+ externalUsedP50: number;
112
+ /**
113
+ * The 90th percentile of the external memory samples, in bytes.
114
+ */
115
+ externalUsedP90: number;
116
+ /**
117
+ * The 95th percentile of the external memory samples, in bytes.
118
+ */
119
+ externalUsedP95: number;
120
+ /**
121
+ * The 99th percentile of the external memory samples, in bytes.
122
+ */
123
+ externalUsedP99: number;
124
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * A monitor: a component exposing the statistics of what it measures.
3
+ * @typeParam T The type of its statistics.
4
+ */
5
+ export interface IMonitor<T> {
6
+ /**
7
+ * The statistics of the measures, computed at each read.
8
+ */
9
+ statistics: T;
10
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The statistics of the samples of a measure, in a constant memory: the latest value, the minimum, the maximum, the
3
+ * mean and the standard deviation are exact, the percentiles come from a histogram (3 significant digits)
4
+ */
5
+ export declare class Samples {
6
+ #private;
7
+ protected readonly scale: number;
8
+ /**
9
+ * @param scale the factor of the precision of the percentiles (100 keeps 2 decimals)
10
+ */
11
+ constructor(scale?: number);
12
+ /**
13
+ * Record a sample (a negative value is recorded as 0)
14
+ * @param value
15
+ */
16
+ record(value: number): void;
17
+ /**
18
+ * The number of samples
19
+ */
20
+ get count(): number;
21
+ /**
22
+ * The latest sample, 0 without sample
23
+ */
24
+ get latest(): number;
25
+ /**
26
+ * The minimum, 0 without sample
27
+ */
28
+ get min(): number;
29
+ /**
30
+ * The maximum, 0 without sample
31
+ */
32
+ get max(): number;
33
+ /**
34
+ * The mean, 0 without sample
35
+ */
36
+ get mean(): number;
37
+ /**
38
+ * The (population) standard deviation, 0 without sample
39
+ */
40
+ get stdDev(): number;
41
+ /**
42
+ * A percentile, 0 without sample
43
+ * @param percentile from 0 to 100
44
+ */
45
+ percentile(percentile: number): number;
46
+ /**
47
+ * Forget the samples
48
+ */
49
+ reset(): void;
50
+ }
51
+ /**
52
+ * Round a number to a number of decimals
53
+ * @param value
54
+ * @param fractionDigits
55
+ * @constructor
56
+ */
57
+ export declare function round(value: number, fractionDigits: number): number;
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@lakutata/monitor",
3
+ "version": "3.0.0-beta.0",
4
+ "description": "The monitors of the lakutata framework: the CPU, the memory, the event loop and the HTTP requests of the process, on the histograms of Node.js.",
5
+ "type": "module",
6
+ "main": "./dist/cjs/exports/Monitor.js",
7
+ "types": "./dist/cjs/exports/Monitor.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./dist/types/exports/Monitor.d.ts",
12
+ "default": "./dist/esm/exports/Monitor.js"
13
+ },
14
+ "require": {
15
+ "types": "./dist/cjs/exports/Monitor.d.ts",
16
+ "default": "./dist/cjs/exports/Monitor.js"
17
+ }
18
+ },
19
+ "./package.json": "./package.json"
20
+ },
21
+ "scripts": {
22
+ "clean": "node -e \"require('node:fs').rmSync('dist', {recursive: true, force: true})\"",
23
+ "build:cjs": "tsc -p tsconfig.cjs.json",
24
+ "build:esm": "tsc -p tsconfig.esm.json",
25
+ "build:cjs-pkg": "node ../../scripts/package-build/cjs-package.mjs",
26
+ "build:js": "npm run build:cjs && npm run build:esm && npm run build:cjs-pkg",
27
+ "rebuild": "npm run clean && npm run build:js",
28
+ "compile": "npm run rebuild",
29
+ "test:unit": "npm run compile && node --test \"dist/esm/tests/*.spec.js\""
30
+ },
31
+ "peerDependencies": {
32
+ "@lakutata/core": "3.0.0-beta.0"
33
+ },
34
+ "engines": {
35
+ "node": ">=22"
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "!dist/*/tests",
40
+ "*.md",
41
+ "LICENSE",
42
+ "package.json"
43
+ ]
44
+ }