@pawells/nestjs-prometheus 1.0.0-dev.3052c75
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/LICENSE +21 -0
- package/README.md +290 -0
- package/build/LICENSE +21 -0
- package/build/README.md +290 -0
- package/build/controllers/metrics.controller.d.ts +84 -0
- package/build/controllers/metrics.controller.d.ts.map +1 -0
- package/build/controllers/metrics.controller.js +117 -0
- package/build/controllers/metrics.controller.js.map +1 -0
- package/build/index.d.ts +23 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +23 -0
- package/build/index.js.map +1 -0
- package/build/package.json +63 -0
- package/build/prometheus.exporter.d.ts +187 -0
- package/build/prometheus.exporter.d.ts.map +1 -0
- package/build/prometheus.exporter.js +380 -0
- package/build/prometheus.exporter.js.map +1 -0
- package/build/prometheus.module.d.ts +89 -0
- package/build/prometheus.module.d.ts.map +1 -0
- package/build/prometheus.module.js +122 -0
- package/build/prometheus.module.js.map +1 -0
- package/build/test-setup.d.ts +2 -0
- package/build/test-setup.d.ts.map +1 -0
- package/build/test-setup.js +4 -0
- package/build/test-setup.js.map +1 -0
- package/package.json +80 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aaron Wells
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# NestJS Prometheus Module
|
|
2
|
+
|
|
3
|
+
[](https://github.com/PhillipAWells/nestjs-common/releases)
|
|
4
|
+
[](https://github.com/PhillipAWells/nestjs-common/actions/workflows/ci.yml)
|
|
5
|
+
[](https://www.npmjs.com/package/@pawells/nestjs-prometheus)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
[](./LICENSE)
|
|
8
|
+
[](https://github.com/sponsors/PhillipAWells)
|
|
9
|
+
|
|
10
|
+
Prometheus metrics exporter for NestJS with `/metrics` endpoint, integrated with `@pawells/nestjs-shared` InstrumentationRegistry for event-based metric collection.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
yarn add @pawells/nestjs-prometheus prom-client
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
- **Node.js**: >= 24.0.0
|
|
21
|
+
- **NestJS**: >= 10.0.0
|
|
22
|
+
- **prom-client**: >= 15.0.0
|
|
23
|
+
- **@pawells/nestjs-shared**: same version
|
|
24
|
+
|
|
25
|
+
## Peer Dependencies
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"@nestjs/common": ">=10.0.0",
|
|
30
|
+
"prom-client": ">=15.0.0"
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
### Module Setup
|
|
37
|
+
|
|
38
|
+
Import `PrometheusModule` in your application module:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { Module } from '@nestjs/common';
|
|
42
|
+
import { PrometheusModule } from '@pawells/nestjs-prometheus';
|
|
43
|
+
|
|
44
|
+
@Module({
|
|
45
|
+
imports: [PrometheusModule.forRoot()],
|
|
46
|
+
})
|
|
47
|
+
export class AppModule {}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This automatically registers the PrometheusExporter globally and exposes metrics at `GET /metrics` in Prometheus text format.
|
|
51
|
+
|
|
52
|
+
## How It Works
|
|
53
|
+
|
|
54
|
+
The module integrates with `@pawells/nestjs-shared`'s `InstrumentationRegistry` to:
|
|
55
|
+
|
|
56
|
+
1. **Register descriptors**: When a metric is registered with the InstrumentationRegistry, PrometheusExporter pre-creates the corresponding prom-client instrument (Counter, Gauge, or Histogram)
|
|
57
|
+
2. **Buffer values**: Metric values are buffered in memory as they are recorded
|
|
58
|
+
3. **Flush on pull**: When the `/metrics` endpoint is scraped, all pending values are flushed into prom-client instruments and returned in Prometheus text format
|
|
59
|
+
4. **Cleanup**: On shutdown, the registry and internal caches are cleared
|
|
60
|
+
|
|
61
|
+
This event-based design decouples metric recording from Prometheus scraping, preventing performance overhead on high-frequency metrics.
|
|
62
|
+
|
|
63
|
+
## The /metrics Endpoint
|
|
64
|
+
|
|
65
|
+
The module automatically exposes:
|
|
66
|
+
|
|
67
|
+
- **Endpoint**: `GET /metrics`
|
|
68
|
+
- **Content-Type**: `text/plain; version=0.0.4; charset=utf-8`
|
|
69
|
+
- **Headers**: `X-Robots-Tag: noindex, nofollow` (prevents indexing)
|
|
70
|
+
- **Authentication**: Protected by `MetricsGuard` from `@pawells/nestjs-shared`
|
|
71
|
+
|
|
72
|
+
### Authentication
|
|
73
|
+
|
|
74
|
+
The `/metrics` endpoint respects the optional `METRICS_API_KEY` environment variable:
|
|
75
|
+
|
|
76
|
+
- If **not set**: All requests are allowed
|
|
77
|
+
- If **set**: Requires Bearer token, X-API-Key header, or `?key=` query parameter
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# With METRICS_API_KEY=secret123
|
|
81
|
+
curl -H "Authorization: Bearer secret123" http://localhost:3000/metrics
|
|
82
|
+
curl -H "X-API-Key: secret123" http://localhost:3000/metrics
|
|
83
|
+
curl "http://localhost:3000/metrics?key=secret123"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Exported Metrics
|
|
87
|
+
|
|
88
|
+
The exporter provides three types of metrics:
|
|
89
|
+
|
|
90
|
+
### Node.js Default Metrics
|
|
91
|
+
Automatically collected via `prom-client`:
|
|
92
|
+
- `process_cpu_user_seconds_total` - User CPU time
|
|
93
|
+
- `process_cpu_system_seconds_total` - System CPU time
|
|
94
|
+
- `process_resident_memory_bytes` - RSS memory usage
|
|
95
|
+
- `nodejs_eventloop_delay_seconds` - Event loop delay
|
|
96
|
+
- `nodejs_gc_duration_seconds` - Garbage collection duration
|
|
97
|
+
- And many more...
|
|
98
|
+
|
|
99
|
+
### Custom Metrics
|
|
100
|
+
Applications can register custom metrics with the InstrumentationRegistry and they will be exported automatically.
|
|
101
|
+
|
|
102
|
+
### Example Metrics Output
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
# HELP process_cpu_user_seconds_total Total user CPU time spent in seconds.
|
|
106
|
+
# TYPE process_cpu_user_seconds_total counter
|
|
107
|
+
process_cpu_user_seconds_total 12.345
|
|
108
|
+
|
|
109
|
+
# HELP http_request_duration_seconds HTTP request duration in seconds
|
|
110
|
+
# TYPE http_request_duration_seconds histogram
|
|
111
|
+
http_request_duration_seconds_bucket{le="0.001",method="GET",route="/api/users"} 0
|
|
112
|
+
http_request_duration_seconds_bucket{le="0.01",method="GET",route="/api/users"} 5
|
|
113
|
+
http_request_duration_seconds_bucket{le="0.1",method="GET",route="/api/users"} 42
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Accessing Metrics
|
|
117
|
+
|
|
118
|
+
### Via Prometheus Scraper
|
|
119
|
+
|
|
120
|
+
Configure Prometheus to scrape your application:
|
|
121
|
+
|
|
122
|
+
```yaml
|
|
123
|
+
# prometheus.yml
|
|
124
|
+
scrape_configs:
|
|
125
|
+
- job_name: 'my-app'
|
|
126
|
+
static_configs:
|
|
127
|
+
- targets: ['localhost:3000']
|
|
128
|
+
metrics_path: '/metrics'
|
|
129
|
+
scrape_interval: 15s
|
|
130
|
+
# Optional: Add auth if METRICS_API_KEY is set
|
|
131
|
+
authorization:
|
|
132
|
+
credentials: 'secret123'
|
|
133
|
+
type: Bearer
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Manual HTTP Request
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
curl http://localhost:3000/metrics
|
|
140
|
+
|
|
141
|
+
# With authentication
|
|
142
|
+
curl -H "Authorization: Bearer secret123" http://localhost:3000/metrics
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Integration with @pawells/nestjs-shared HTTPMetricsInterceptor
|
|
146
|
+
|
|
147
|
+
Combine PrometheusModule with HTTPMetricsInterceptor for automatic HTTP metrics:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { Module } from '@nestjs/common';
|
|
151
|
+
import { APP_INTERCEPTOR } from '@nestjs/core';
|
|
152
|
+
import { PrometheusModule } from '@pawells/nestjs-prometheus';
|
|
153
|
+
import { HTTPMetricsInterceptor } from '@pawells/nestjs-shared';
|
|
154
|
+
|
|
155
|
+
@Module({
|
|
156
|
+
imports: [PrometheusModule.forRoot()],
|
|
157
|
+
providers: [
|
|
158
|
+
{
|
|
159
|
+
provide: APP_INTERCEPTOR,
|
|
160
|
+
useClass: HTTPMetricsInterceptor,
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
})
|
|
164
|
+
export class AppModule {}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
This automatically tracks:
|
|
168
|
+
- **Request count** by method and route
|
|
169
|
+
- **Request duration** (histogram with default buckets)
|
|
170
|
+
- **Response status codes**
|
|
171
|
+
- **Path normalization** (dynamic segments like UUIDs are normalized to `:id`)
|
|
172
|
+
|
|
173
|
+
## Module API
|
|
174
|
+
|
|
175
|
+
### PrometheusModule.forRoot()
|
|
176
|
+
|
|
177
|
+
Returns a DynamicModule configured as global, enabling single import at the top level:
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
@Module({
|
|
181
|
+
imports: [PrometheusModule.forRoot()],
|
|
182
|
+
})
|
|
183
|
+
export class AppModule {}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Class Reference
|
|
187
|
+
|
|
188
|
+
### PrometheusModule
|
|
189
|
+
|
|
190
|
+
The main NestJS module. Implements `OnModuleInit` and `OnApplicationShutdown`.
|
|
191
|
+
|
|
192
|
+
**Static Methods:**
|
|
193
|
+
- `forRoot()` - Create global module with automatic registration
|
|
194
|
+
|
|
195
|
+
**Lifecycle Methods:**
|
|
196
|
+
- `onModuleInit()` - Registers the exporter with InstrumentationRegistry
|
|
197
|
+
- `onApplicationShutdown()` - Calls exporter.shutdown() to clean up resources
|
|
198
|
+
|
|
199
|
+
### PrometheusExporter
|
|
200
|
+
|
|
201
|
+
Implements `IMetricsExporter` from `@pawells/nestjs-shared`.
|
|
202
|
+
|
|
203
|
+
**Properties:**
|
|
204
|
+
- `supportsEventBased` - `true` (buffers metric values)
|
|
205
|
+
- `supportsPull` - `true` (supports pull-based retrieval)
|
|
206
|
+
|
|
207
|
+
**Methods:**
|
|
208
|
+
- `onDescriptorRegistered(descriptor: MetricDescriptor)` - Called when a metric is registered; creates the appropriate prom-client instrument
|
|
209
|
+
- `onMetricRecorded(value: MetricValue)` - Buffers a metric value to be flushed on next pull
|
|
210
|
+
- `getMetrics(): Promise<string>` - Flushes pending values and returns metrics in Prometheus text format
|
|
211
|
+
- `shutdown(): Promise<void>` - Clears registry and releases resources
|
|
212
|
+
|
|
213
|
+
**Internals:**
|
|
214
|
+
- Maintains a prom-client `Registry` for instrument management
|
|
215
|
+
- Buffers metric values per metric (max 1000 per metric to prevent unbounded memory growth)
|
|
216
|
+
- Atomically swaps pending arrays during flush to prevent data loss on concurrent records
|
|
217
|
+
|
|
218
|
+
### MetricsController
|
|
219
|
+
|
|
220
|
+
HTTP controller with single endpoint.
|
|
221
|
+
|
|
222
|
+
**Methods:**
|
|
223
|
+
- `getMetrics(response: Response): Promise<void>` - GET /metrics, protected by MetricsGuard
|
|
224
|
+
|
|
225
|
+
## Advanced Patterns
|
|
226
|
+
|
|
227
|
+
### Custom Metrics via InstrumentationRegistry
|
|
228
|
+
|
|
229
|
+
Register custom metrics with `@pawells/nestjs-shared`:
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { Injectable } from '@nestjs/common';
|
|
233
|
+
import { InstrumentationRegistry } from '@pawells/nestjs-shared';
|
|
234
|
+
|
|
235
|
+
@Injectable()
|
|
236
|
+
export class OrderService {
|
|
237
|
+
constructor(private readonly registry: InstrumentationRegistry) {}
|
|
238
|
+
|
|
239
|
+
trackOrderCreation(status: string) {
|
|
240
|
+
// Register descriptor (once)
|
|
241
|
+
this.registry.registerDescriptor({
|
|
242
|
+
name: 'orders_created_total',
|
|
243
|
+
type: 'counter',
|
|
244
|
+
help: 'Total orders created',
|
|
245
|
+
labelNames: ['status'],
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Record value
|
|
249
|
+
this.registry.recordMetric({
|
|
250
|
+
descriptor: { name: 'orders_created_total' } as MetricDescriptor,
|
|
251
|
+
value: 1,
|
|
252
|
+
labels: { status },
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
When metrics are pulled from `/metrics`, they are automatically exported in Prometheus format.
|
|
259
|
+
|
|
260
|
+
## Metric Types
|
|
261
|
+
|
|
262
|
+
The PrometheusExporter supports these metric types (as defined in MetricDescriptor):
|
|
263
|
+
|
|
264
|
+
| Type | Mapping | Usage |
|
|
265
|
+
|------|---------|-------|
|
|
266
|
+
| `counter` | prom-client Counter | Monotonically increasing values (e.g., request count) |
|
|
267
|
+
| `gauge` | prom-client Gauge | Point-in-time values (e.g., memory usage) |
|
|
268
|
+
| `updown_counter` | prom-client Gauge | Values that can increase or decrease |
|
|
269
|
+
| `histogram` | prom-client Histogram | Distribution of values (e.g., request latency) |
|
|
270
|
+
|
|
271
|
+
### Gauge and UpDownCounter Accumulation Behavior
|
|
272
|
+
|
|
273
|
+
**Gauge (`gauge`)**: Records a point-in-time snapshot value. Each recorded value overwrites the previous one for a given label set.
|
|
274
|
+
|
|
275
|
+
**UpDownCounter (`updown_counter`)**: Accumulates values across scrapes. The PrometheusExporter maintains a running-total Map (`gaugeValues`) for each updown_counter metric, keyed by normalized label set. When a value is recorded:
|
|
276
|
+
1. All values for the same label set within a single scrape interval are accumulated together
|
|
277
|
+
2. The accumulated sum is then added to the persistent running total
|
|
278
|
+
3. This running total persists across Prometheus scrapes (resets only on exporter shutdown)
|
|
279
|
+
|
|
280
|
+
Example: If you record `+5` then `+3` for the same labels in one scrape cycle, the running total increases by `8` (not just `3`). On the next scrape, if you record `+2`, the running total increases by another `2`.
|
|
281
|
+
|
|
282
|
+
## Related Packages
|
|
283
|
+
|
|
284
|
+
- **[@pawells/nestjs-shared](https://www.npmjs.com/package/@pawells/nestjs-shared)** - Foundation: InstrumentationRegistry, HTTPMetricsInterceptor, MetricsGuard
|
|
285
|
+
- **[@pawells/nestjs-open-telemetry](https://www.npmjs.com/package/@pawells/nestjs-open-telemetry)** - OpenTelemetry tracing and metrics integration
|
|
286
|
+
- **[prom-client](https://github.com/siimon/prom-client)** - Official Prometheus client library for Node.js
|
|
287
|
+
|
|
288
|
+
## License
|
|
289
|
+
|
|
290
|
+
MIT
|
package/build/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aaron Wells
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/build/README.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# NestJS Prometheus Module
|
|
2
|
+
|
|
3
|
+
[](https://github.com/PhillipAWells/nestjs-common/releases)
|
|
4
|
+
[](https://github.com/PhillipAWells/nestjs-common/actions/workflows/ci.yml)
|
|
5
|
+
[](https://www.npmjs.com/package/@pawells/nestjs-prometheus)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
[](./LICENSE)
|
|
8
|
+
[](https://github.com/sponsors/PhillipAWells)
|
|
9
|
+
|
|
10
|
+
Prometheus metrics exporter for NestJS with `/metrics` endpoint, integrated with `@pawells/nestjs-shared` InstrumentationRegistry for event-based metric collection.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
yarn add @pawells/nestjs-prometheus prom-client
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
- **Node.js**: >= 24.0.0
|
|
21
|
+
- **NestJS**: >= 10.0.0
|
|
22
|
+
- **prom-client**: >= 15.0.0
|
|
23
|
+
- **@pawells/nestjs-shared**: same version
|
|
24
|
+
|
|
25
|
+
## Peer Dependencies
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"@nestjs/common": ">=10.0.0",
|
|
30
|
+
"prom-client": ">=15.0.0"
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
### Module Setup
|
|
37
|
+
|
|
38
|
+
Import `PrometheusModule` in your application module:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { Module } from '@nestjs/common';
|
|
42
|
+
import { PrometheusModule } from '@pawells/nestjs-prometheus';
|
|
43
|
+
|
|
44
|
+
@Module({
|
|
45
|
+
imports: [PrometheusModule.forRoot()],
|
|
46
|
+
})
|
|
47
|
+
export class AppModule {}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This automatically registers the PrometheusExporter globally and exposes metrics at `GET /metrics` in Prometheus text format.
|
|
51
|
+
|
|
52
|
+
## How It Works
|
|
53
|
+
|
|
54
|
+
The module integrates with `@pawells/nestjs-shared`'s `InstrumentationRegistry` to:
|
|
55
|
+
|
|
56
|
+
1. **Register descriptors**: When a metric is registered with the InstrumentationRegistry, PrometheusExporter pre-creates the corresponding prom-client instrument (Counter, Gauge, or Histogram)
|
|
57
|
+
2. **Buffer values**: Metric values are buffered in memory as they are recorded
|
|
58
|
+
3. **Flush on pull**: When the `/metrics` endpoint is scraped, all pending values are flushed into prom-client instruments and returned in Prometheus text format
|
|
59
|
+
4. **Cleanup**: On shutdown, the registry and internal caches are cleared
|
|
60
|
+
|
|
61
|
+
This event-based design decouples metric recording from Prometheus scraping, preventing performance overhead on high-frequency metrics.
|
|
62
|
+
|
|
63
|
+
## The /metrics Endpoint
|
|
64
|
+
|
|
65
|
+
The module automatically exposes:
|
|
66
|
+
|
|
67
|
+
- **Endpoint**: `GET /metrics`
|
|
68
|
+
- **Content-Type**: `text/plain; version=0.0.4; charset=utf-8`
|
|
69
|
+
- **Headers**: `X-Robots-Tag: noindex, nofollow` (prevents indexing)
|
|
70
|
+
- **Authentication**: Protected by `MetricsGuard` from `@pawells/nestjs-shared`
|
|
71
|
+
|
|
72
|
+
### Authentication
|
|
73
|
+
|
|
74
|
+
The `/metrics` endpoint respects the optional `METRICS_API_KEY` environment variable:
|
|
75
|
+
|
|
76
|
+
- If **not set**: All requests are allowed
|
|
77
|
+
- If **set**: Requires Bearer token, X-API-Key header, or `?key=` query parameter
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# With METRICS_API_KEY=secret123
|
|
81
|
+
curl -H "Authorization: Bearer secret123" http://localhost:3000/metrics
|
|
82
|
+
curl -H "X-API-Key: secret123" http://localhost:3000/metrics
|
|
83
|
+
curl "http://localhost:3000/metrics?key=secret123"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Exported Metrics
|
|
87
|
+
|
|
88
|
+
The exporter provides three types of metrics:
|
|
89
|
+
|
|
90
|
+
### Node.js Default Metrics
|
|
91
|
+
Automatically collected via `prom-client`:
|
|
92
|
+
- `process_cpu_user_seconds_total` - User CPU time
|
|
93
|
+
- `process_cpu_system_seconds_total` - System CPU time
|
|
94
|
+
- `process_resident_memory_bytes` - RSS memory usage
|
|
95
|
+
- `nodejs_eventloop_delay_seconds` - Event loop delay
|
|
96
|
+
- `nodejs_gc_duration_seconds` - Garbage collection duration
|
|
97
|
+
- And many more...
|
|
98
|
+
|
|
99
|
+
### Custom Metrics
|
|
100
|
+
Applications can register custom metrics with the InstrumentationRegistry and they will be exported automatically.
|
|
101
|
+
|
|
102
|
+
### Example Metrics Output
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
# HELP process_cpu_user_seconds_total Total user CPU time spent in seconds.
|
|
106
|
+
# TYPE process_cpu_user_seconds_total counter
|
|
107
|
+
process_cpu_user_seconds_total 12.345
|
|
108
|
+
|
|
109
|
+
# HELP http_request_duration_seconds HTTP request duration in seconds
|
|
110
|
+
# TYPE http_request_duration_seconds histogram
|
|
111
|
+
http_request_duration_seconds_bucket{le="0.001",method="GET",route="/api/users"} 0
|
|
112
|
+
http_request_duration_seconds_bucket{le="0.01",method="GET",route="/api/users"} 5
|
|
113
|
+
http_request_duration_seconds_bucket{le="0.1",method="GET",route="/api/users"} 42
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Accessing Metrics
|
|
117
|
+
|
|
118
|
+
### Via Prometheus Scraper
|
|
119
|
+
|
|
120
|
+
Configure Prometheus to scrape your application:
|
|
121
|
+
|
|
122
|
+
```yaml
|
|
123
|
+
# prometheus.yml
|
|
124
|
+
scrape_configs:
|
|
125
|
+
- job_name: 'my-app'
|
|
126
|
+
static_configs:
|
|
127
|
+
- targets: ['localhost:3000']
|
|
128
|
+
metrics_path: '/metrics'
|
|
129
|
+
scrape_interval: 15s
|
|
130
|
+
# Optional: Add auth if METRICS_API_KEY is set
|
|
131
|
+
authorization:
|
|
132
|
+
credentials: 'secret123'
|
|
133
|
+
type: Bearer
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Manual HTTP Request
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
curl http://localhost:3000/metrics
|
|
140
|
+
|
|
141
|
+
# With authentication
|
|
142
|
+
curl -H "Authorization: Bearer secret123" http://localhost:3000/metrics
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Integration with @pawells/nestjs-shared HTTPMetricsInterceptor
|
|
146
|
+
|
|
147
|
+
Combine PrometheusModule with HTTPMetricsInterceptor for automatic HTTP metrics:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { Module } from '@nestjs/common';
|
|
151
|
+
import { APP_INTERCEPTOR } from '@nestjs/core';
|
|
152
|
+
import { PrometheusModule } from '@pawells/nestjs-prometheus';
|
|
153
|
+
import { HTTPMetricsInterceptor } from '@pawells/nestjs-shared';
|
|
154
|
+
|
|
155
|
+
@Module({
|
|
156
|
+
imports: [PrometheusModule.forRoot()],
|
|
157
|
+
providers: [
|
|
158
|
+
{
|
|
159
|
+
provide: APP_INTERCEPTOR,
|
|
160
|
+
useClass: HTTPMetricsInterceptor,
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
})
|
|
164
|
+
export class AppModule {}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
This automatically tracks:
|
|
168
|
+
- **Request count** by method and route
|
|
169
|
+
- **Request duration** (histogram with default buckets)
|
|
170
|
+
- **Response status codes**
|
|
171
|
+
- **Path normalization** (dynamic segments like UUIDs are normalized to `:id`)
|
|
172
|
+
|
|
173
|
+
## Module API
|
|
174
|
+
|
|
175
|
+
### PrometheusModule.forRoot()
|
|
176
|
+
|
|
177
|
+
Returns a DynamicModule configured as global, enabling single import at the top level:
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
@Module({
|
|
181
|
+
imports: [PrometheusModule.forRoot()],
|
|
182
|
+
})
|
|
183
|
+
export class AppModule {}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Class Reference
|
|
187
|
+
|
|
188
|
+
### PrometheusModule
|
|
189
|
+
|
|
190
|
+
The main NestJS module. Implements `OnModuleInit` and `OnApplicationShutdown`.
|
|
191
|
+
|
|
192
|
+
**Static Methods:**
|
|
193
|
+
- `forRoot()` - Create global module with automatic registration
|
|
194
|
+
|
|
195
|
+
**Lifecycle Methods:**
|
|
196
|
+
- `onModuleInit()` - Registers the exporter with InstrumentationRegistry
|
|
197
|
+
- `onApplicationShutdown()` - Calls exporter.shutdown() to clean up resources
|
|
198
|
+
|
|
199
|
+
### PrometheusExporter
|
|
200
|
+
|
|
201
|
+
Implements `IMetricsExporter` from `@pawells/nestjs-shared`.
|
|
202
|
+
|
|
203
|
+
**Properties:**
|
|
204
|
+
- `supportsEventBased` - `true` (buffers metric values)
|
|
205
|
+
- `supportsPull` - `true` (supports pull-based retrieval)
|
|
206
|
+
|
|
207
|
+
**Methods:**
|
|
208
|
+
- `onDescriptorRegistered(descriptor: MetricDescriptor)` - Called when a metric is registered; creates the appropriate prom-client instrument
|
|
209
|
+
- `onMetricRecorded(value: MetricValue)` - Buffers a metric value to be flushed on next pull
|
|
210
|
+
- `getMetrics(): Promise<string>` - Flushes pending values and returns metrics in Prometheus text format
|
|
211
|
+
- `shutdown(): Promise<void>` - Clears registry and releases resources
|
|
212
|
+
|
|
213
|
+
**Internals:**
|
|
214
|
+
- Maintains a prom-client `Registry` for instrument management
|
|
215
|
+
- Buffers metric values per metric (max 1000 per metric to prevent unbounded memory growth)
|
|
216
|
+
- Atomically swaps pending arrays during flush to prevent data loss on concurrent records
|
|
217
|
+
|
|
218
|
+
### MetricsController
|
|
219
|
+
|
|
220
|
+
HTTP controller with single endpoint.
|
|
221
|
+
|
|
222
|
+
**Methods:**
|
|
223
|
+
- `getMetrics(response: Response): Promise<void>` - GET /metrics, protected by MetricsGuard
|
|
224
|
+
|
|
225
|
+
## Advanced Patterns
|
|
226
|
+
|
|
227
|
+
### Custom Metrics via InstrumentationRegistry
|
|
228
|
+
|
|
229
|
+
Register custom metrics with `@pawells/nestjs-shared`:
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { Injectable } from '@nestjs/common';
|
|
233
|
+
import { InstrumentationRegistry } from '@pawells/nestjs-shared';
|
|
234
|
+
|
|
235
|
+
@Injectable()
|
|
236
|
+
export class OrderService {
|
|
237
|
+
constructor(private readonly registry: InstrumentationRegistry) {}
|
|
238
|
+
|
|
239
|
+
trackOrderCreation(status: string) {
|
|
240
|
+
// Register descriptor (once)
|
|
241
|
+
this.registry.registerDescriptor({
|
|
242
|
+
name: 'orders_created_total',
|
|
243
|
+
type: 'counter',
|
|
244
|
+
help: 'Total orders created',
|
|
245
|
+
labelNames: ['status'],
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Record value
|
|
249
|
+
this.registry.recordMetric({
|
|
250
|
+
descriptor: { name: 'orders_created_total' } as MetricDescriptor,
|
|
251
|
+
value: 1,
|
|
252
|
+
labels: { status },
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
When metrics are pulled from `/metrics`, they are automatically exported in Prometheus format.
|
|
259
|
+
|
|
260
|
+
## Metric Types
|
|
261
|
+
|
|
262
|
+
The PrometheusExporter supports these metric types (as defined in MetricDescriptor):
|
|
263
|
+
|
|
264
|
+
| Type | Mapping | Usage |
|
|
265
|
+
|------|---------|-------|
|
|
266
|
+
| `counter` | prom-client Counter | Monotonically increasing values (e.g., request count) |
|
|
267
|
+
| `gauge` | prom-client Gauge | Point-in-time values (e.g., memory usage) |
|
|
268
|
+
| `updown_counter` | prom-client Gauge | Values that can increase or decrease |
|
|
269
|
+
| `histogram` | prom-client Histogram | Distribution of values (e.g., request latency) |
|
|
270
|
+
|
|
271
|
+
### Gauge and UpDownCounter Accumulation Behavior
|
|
272
|
+
|
|
273
|
+
**Gauge (`gauge`)**: Records a point-in-time snapshot value. Each recorded value overwrites the previous one for a given label set.
|
|
274
|
+
|
|
275
|
+
**UpDownCounter (`updown_counter`)**: Accumulates values across scrapes. The PrometheusExporter maintains a running-total Map (`gaugeValues`) for each updown_counter metric, keyed by normalized label set. When a value is recorded:
|
|
276
|
+
1. All values for the same label set within a single scrape interval are accumulated together
|
|
277
|
+
2. The accumulated sum is then added to the persistent running total
|
|
278
|
+
3. This running total persists across Prometheus scrapes (resets only on exporter shutdown)
|
|
279
|
+
|
|
280
|
+
Example: If you record `+5` then `+3` for the same labels in one scrape cycle, the running total increases by `8` (not just `3`). On the next scrape, if you record `+2`, the running total increases by another `2`.
|
|
281
|
+
|
|
282
|
+
## Related Packages
|
|
283
|
+
|
|
284
|
+
- **[@pawells/nestjs-shared](https://www.npmjs.com/package/@pawells/nestjs-shared)** - Foundation: InstrumentationRegistry, HTTPMetricsInterceptor, MetricsGuard
|
|
285
|
+
- **[@pawells/nestjs-open-telemetry](https://www.npmjs.com/package/@pawells/nestjs-open-telemetry)** - OpenTelemetry tracing and metrics integration
|
|
286
|
+
- **[prom-client](https://github.com/siimon/prom-client)** - Official Prometheus client library for Node.js
|
|
287
|
+
|
|
288
|
+
## License
|
|
289
|
+
|
|
290
|
+
MIT
|