@fluojs/metrics 1.0.3 → 1.0.4
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.ko.md +43 -6
- package/README.md +43 -6
- package/dist/metrics-module.d.ts +1 -0
- package/dist/metrics-module.d.ts.map +1 -1
- package/dist/metrics-module.js +66 -30
- package/package.json +5 -4
package/README.ko.md
CHANGED
|
@@ -20,6 +20,10 @@ HTTP metric과 platform telemetry를 포함해 fluo 애플리케이션을 위한
|
|
|
20
20
|
pnpm add @fluojs/metrics
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
## 요구 사항
|
|
24
|
+
|
|
25
|
+
`@fluojs/metrics`는 Node.js 20 이상에서 실행됩니다. package manifest는 `engines.node >=20.0.0`을 선언합니다.
|
|
26
|
+
|
|
23
27
|
## 사용 시점
|
|
24
28
|
|
|
25
29
|
- 애플리케이션이 Prometheus-compatible scraping을 위한 `/metrics` endpoint를 노출해야 할 때
|
|
@@ -40,17 +44,18 @@ class AppModule {}
|
|
|
40
44
|
|
|
41
45
|
`MetricsModule.forRoot()`는 기본적으로 `GET /metrics`를 노출합니다. HTTP request instrumentation middleware를 설치하려면 `http: true` 또는 `http` option object를 전달하세요. HTTP 계측이 활성화되면 request total, error count, request duration을 기록합니다. 운영 환경에서는 scrape endpoint boundary를 명시적으로 다루세요. platform-level proxy가 준비될 때까지 `path: false`로 끄거나 dedicated endpoint middleware를 연결할 수 있습니다.
|
|
42
46
|
|
|
43
|
-
Scrape endpoint는 active `prom-client` Registry output을 해당 Registry의 Prometheus content type으로 반환합니다. `MetricsModule.forRoot()`는 `registry` option을 전달하지 않는 한 격리된 Registry를 생성합니다. framework metric과 application-defined metric이 하나의 scrape surface를 의도적으로 공유해야 할 때만 shared `Registry`를 전달하세요.
|
|
47
|
+
Scrape endpoint는 active `prom-client` Registry output을 해당 Registry의 Prometheus content type으로 반환합니다. `MetricsModule.forRoot()`는 `registry` option을 전달하지 않는 한 application bootstrap마다 격리된 Registry를 생성합니다. 같은 dynamic module class를 다른 bootstrap에서 재사용해도 격리된 metric state는 새로 만들어집니다. framework metric과 application-defined metric이 하나의 scrape surface를 의도적으로 공유해야 할 때만 shared `Registry`를 전달하세요.
|
|
44
48
|
|
|
45
49
|
## 공개 책임
|
|
46
50
|
|
|
47
51
|
| 표면 | 책임 | 경계 |
|
|
48
52
|
| --- | --- | --- |
|
|
49
53
|
| `MetricsModule.forRoot(...)` | Prometheus scrape endpoint, default metrics, optional HTTP instrumentation, platform telemetry, registry ownership을 wiring합니다. | `provider`는 현재 `'prometheus'`만 받습니다. `path: false`는 scrape route와 route-scoped endpoint middleware를 비활성화합니다. |
|
|
50
|
-
| `MetricsService` | Active Registry 위에서 custom `Counter`, `Gauge`, `Histogram`을
|
|
51
|
-
| `
|
|
54
|
+
| `MetricsService` | Active Registry 위에서 custom `Counter`, `Gauge`, `Histogram`을 만드는 application-facing facade이며, 고급 Registry 공유를 위한 `getRegistry()`도 제공합니다. | 비즈니스/application metric은 collector helper를 사용하세요. `getRegistry()`는 active `prom-client` Registry를 `MetricsModule.forRoot({ registry })`로 직접 받을 수 없는 integration에 넘겨야 할 때만 사용하세요. |
|
|
55
|
+
| `Registry` | Shared-registry setup을 위한 `prom-client` `Registry` constructor re-export입니다. | 같은 Prometheus Registry 구현체이므로 중복 metric name은 Prometheus semantics에 따라 계속 실패합니다. |
|
|
56
|
+
| `METER_PROVIDER` / `PrometheusMeterProvider` / meter type | Provider token 또는 backend-neutral counter/gauge/histogram facade가 필요한 first-party package integration용 low-level meter bridge입니다. | Application code는 package-level integration을 직접 조합하는 경우가 아니면 보통 이 token이 필요하지 않습니다. 현재 bundled provider backend는 Prometheus뿐입니다. |
|
|
52
57
|
| `middleware` | Framework HTTP metrics와 endpoint-scoped middleware 뒤의 module middleware chain에 참여하는 module-level middleware입니다. | Route-scoped가 아니므로 scrape route만 보호하려면 `endpointMiddleware`를 사용하세요. |
|
|
53
|
-
| `endpointMiddleware` | 설정된 scrape endpoint에만 바인딩되는 class-based `@fluojs/http` middleware constructor입니다. | `path: false`일
|
|
58
|
+
| `endpointMiddleware` | 설정된 scrape endpoint에만 바인딩되는 class-based `@fluojs/http` middleware constructor입니다. | `path: false`일 때만 무시됩니다. `''`를 포함한 모든 문자열 `path`는 활성 endpoint path입니다. 함수나 global middleware declaration은 이 option의 계약 밖입니다. |
|
|
54
59
|
|
|
55
60
|
## 공통 패턴
|
|
56
61
|
|
|
@@ -103,9 +108,39 @@ MetricsModule.forRoot({
|
|
|
103
108
|
|
|
104
109
|
`endpointMiddleware`는 class-based `@fluojs/http` middleware constructor를 받으며 metrics scrape endpoint에만 바인딩됩니다. middleware function이나 global middleware declaration은 이 option의 패키지 계약이 아닙니다. `middleware`는 module-level middleware로 남아 endpoint-scoped middleware 뒤의 module chain에서 실행되고, `endpointMiddleware`는 `path: false`로 scrape route를 비활성화하면 완전히 건너뜁니다. HTTP 계측이 활성화된 경우 endpoint middleware가 던진 실패도 내장 HTTP request/error collector에 기록됩니다.
|
|
105
110
|
|
|
111
|
+
### Custom metric은 한 번 생성하고 재사용하기
|
|
112
|
+
|
|
113
|
+
`MetricsService.counter(...)`, `gauge(...)`, `histogram(...)`은 active Registry에 Prometheus collector를 생성합니다. 각 custom metric은 provider construction 또는 application startup 중 한 번만 만들고, business action이 발생할 때는 반환된 collector를 재사용하세요.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { Inject } from '@fluojs/core';
|
|
117
|
+
import { MetricsService } from '@fluojs/metrics';
|
|
118
|
+
|
|
119
|
+
@Inject(MetricsService)
|
|
120
|
+
class OrdersService {
|
|
121
|
+
private readonly ordersCreated: ReturnType<MetricsService['counter']>;
|
|
122
|
+
|
|
123
|
+
constructor(metrics: MetricsService) {
|
|
124
|
+
this.ordersCreated = metrics.counter({
|
|
125
|
+
name: 'orders_created_total',
|
|
126
|
+
help: 'Total orders created',
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
recordOrderCreated(): void {
|
|
131
|
+
this.ordersCreated.inc();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
같은 이름으로 `MetricsService.counter(...)`를 다시 호출하면 collector를 다시 만들려고 하므로 Prometheus의 duplicate-name failure behavior를 따릅니다. 요청이나 command handler마다 새로 만들지 말고 collector를 저장해 재사용하세요.
|
|
137
|
+
|
|
138
|
+
`MetricsService.getRegistry()`는 module scrape endpoint, 내장 HTTP collector, platform telemetry, service를 통해 만든 custom collector가 함께 사용하는 동일한 active `prom-client` Registry를 반환합니다. Bootstrap을 직접 소유한다면 `MetricsModule.forRoot({ registry })`에 명시적 `registry`를 전달하는 방식을 우선하세요. `getRegistry()`는 DI로 `MetricsService`를 받은 advanced integration이 이미 활성화된 Registry에 third-party Prometheus collector를 등록해야 할 때 사용합니다.
|
|
139
|
+
|
|
106
140
|
### Framework metric과 app metric이 하나의 registry를 공유하기
|
|
107
141
|
|
|
108
142
|
```ts
|
|
143
|
+
import { Module } from '@fluojs/core';
|
|
109
144
|
import { Counter, Registry } from 'prom-client';
|
|
110
145
|
import { MetricsModule } from '@fluojs/metrics';
|
|
111
146
|
|
|
@@ -169,16 +204,18 @@ MetricsModule.forRoot({
|
|
|
169
204
|
## 공개 API
|
|
170
205
|
|
|
171
206
|
- `MetricsModule.forRoot(options)`
|
|
172
|
-
- `MetricsService`
|
|
207
|
+
- `MetricsService` 및 `counter(...)`, `gauge(...)`, `histogram(...)`, `getRegistry()`
|
|
173
208
|
- `METER_PROVIDER` (Token)
|
|
174
209
|
- `PrometheusMeterProvider`
|
|
210
|
+
- Meter abstraction type: `MeterProvider`, `MeterCounter`, `MeterGauge`, `MeterHistogram`
|
|
175
211
|
- `HttpMetricsMiddleware` 및 HTTP path-label 옵션 타입
|
|
176
212
|
- `provider`(현재는 `'prometheus'`만 지원), module-level `middleware`, endpoint-scoped `endpointMiddleware`를 포함한 module option
|
|
177
213
|
- `prom-client`의 `Registry`
|
|
178
214
|
|
|
179
215
|
### 운영 기본값
|
|
180
216
|
|
|
181
|
-
- `path`의 기본값은 `'/metrics'
|
|
217
|
+
- `path`의 기본값은 `'/metrics'`입니다. `''`를 포함한 모든 문자열 path는 scrape endpoint를 노출하며, `path: false`로만 scrape endpoint를 완전히 비활성화할 수 있습니다.
|
|
218
|
+
- `registry`를 생략하면 application bootstrap마다 fresh isolated Registry, `MetricsService`, meter provider, telemetry collector set을 소유합니다.
|
|
182
219
|
- scrape response는 active Registry의 Prometheus content type과 Registry contents를 사용합니다.
|
|
183
220
|
- `defaultMetrics`의 기본값은 `true`이며, `defaultMetrics: false`로 해당 Registry의 Prometheus 기본 프로세스/Node.js collector를 끌 수 있습니다.
|
|
184
221
|
- `endpointMiddleware`는 class-based route-scoped middleware를 스크레이프 엔드포인트에만 바인딩합니다. HTTP 계측이 활성화된 경우 endpoint middleware 실패는 내장 HTTP collector에 집계됩니다.
|
package/README.md
CHANGED
|
@@ -20,6 +20,10 @@ Prometheus metrics exposure for fluo applications, including framework-aware HTT
|
|
|
20
20
|
pnpm add @fluojs/metrics
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
## Requirements
|
|
24
|
+
|
|
25
|
+
`@fluojs/metrics` runs on Node.js 20 or newer; the package manifest declares `engines.node >=20.0.0`.
|
|
26
|
+
|
|
23
27
|
## When to Use
|
|
24
28
|
|
|
25
29
|
- when your app should expose a `/metrics` endpoint for Prometheus-compatible scraping
|
|
@@ -40,17 +44,18 @@ class AppModule {}
|
|
|
40
44
|
|
|
41
45
|
`MetricsModule.forRoot()` exposes `GET /metrics` by default. Pass `http: true` (or an `http` options object) when you want the module to install HTTP request instrumentation middleware. When HTTP instrumentation is enabled, the module records request totals, error counts, and request duration. For production deployments, make the scrape endpoint boundary explicit: either disable it with `path: false` until a platform-level proxy is in place, or attach dedicated endpoint middleware.
|
|
42
46
|
|
|
43
|
-
The scrape endpoint returns the active `prom-client` registry output with that registry's Prometheus content type. `MetricsModule.forRoot()` creates an isolated registry unless you pass a `registry` option;
|
|
47
|
+
The scrape endpoint returns the active `prom-client` registry output with that registry's Prometheus content type. `MetricsModule.forRoot()` creates an isolated registry for each application bootstrap unless you pass a `registry` option; reusing the same dynamic module class for another bootstrap receives fresh isolated metrics state. Pass a shared `Registry` only when framework metrics and application-defined metrics intentionally share one scrape surface.
|
|
44
48
|
|
|
45
49
|
## Public Responsibilities
|
|
46
50
|
|
|
47
51
|
| Surface | Responsibility | Boundary |
|
|
48
52
|
| --- | --- | --- |
|
|
49
53
|
| `MetricsModule.forRoot(...)` | Wires the Prometheus scrape endpoint, default metrics, optional HTTP instrumentation, platform telemetry, and registry ownership. | `provider` currently accepts only `'prometheus'`; `path: false` disables the scrape route and route-scoped endpoint middleware. |
|
|
50
|
-
| `MetricsService` | Application-facing facade for custom `Counter`, `Gauge`, and `Histogram` metrics on the active registry. | Use
|
|
51
|
-
| `
|
|
54
|
+
| `MetricsService` | Application-facing facade for custom `Counter`, `Gauge`, and `Histogram` metrics on the active registry, plus `getRegistry()` for deliberate advanced registry sharing. | Use collector helpers for business/application metrics. Use `getRegistry()` only when an integration must hand the active `prom-client` Registry to code that cannot receive `MetricsModule.forRoot({ registry })` directly. |
|
|
55
|
+
| `Registry` | Re-export of `prom-client`'s `Registry` constructor for shared-registry setups. | It is the same Prometheus registry implementation; duplicate metric names still fail according to Prometheus semantics. |
|
|
56
|
+
| `METER_PROVIDER` / `PrometheusMeterProvider` / meter types | Low-level meter bridge for first-party package integrations that need a provider token or backend-neutral counter/gauge/histogram facade. | Application code usually does not need this token unless it is composing package-level integrations; the only bundled provider backend today is Prometheus. |
|
|
52
57
|
| `middleware` | Module-level middleware that participates in the module middleware chain after framework HTTP metrics and endpoint-scoped middleware. | It is not route-scoped; use `endpointMiddleware` when only the scrape route should be protected. |
|
|
53
|
-
| `endpointMiddleware` | Class-based `@fluojs/http` middleware constructors bound only to the configured scrape endpoint. | Ignored when `path: false`;
|
|
58
|
+
| `endpointMiddleware` | Class-based `@fluojs/http` middleware constructors bound only to the configured scrape endpoint. | Ignored only when `path: false`; any string `path`, including `''`, remains an active endpoint path. Functions or global middleware declarations are outside this option's contract. |
|
|
54
59
|
|
|
55
60
|
## Common Patterns
|
|
56
61
|
|
|
@@ -103,9 +108,39 @@ MetricsModule.forRoot({
|
|
|
103
108
|
|
|
104
109
|
`endpointMiddleware` accepts class-based `@fluojs/http` middleware constructors and binds them only to the metrics scrape endpoint. Middleware functions or global middleware declarations are not the package contract for this option. `middleware` remains module-level middleware and runs as part of the module chain after endpoint-scoped middleware, while `endpointMiddleware` is skipped entirely when `path: false` disables the scrape route. When HTTP instrumentation is enabled, failures thrown by endpoint middleware are recorded in the built-in HTTP request and error collectors.
|
|
105
110
|
|
|
111
|
+
### Create custom metrics once and reuse them
|
|
112
|
+
|
|
113
|
+
`MetricsService.counter(...)`, `gauge(...)`, and `histogram(...)` create Prometheus collectors on the active registry. Create each custom metric once during provider construction or application startup, then reuse the returned collector when business actions occur.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { Inject } from '@fluojs/core';
|
|
117
|
+
import { MetricsService } from '@fluojs/metrics';
|
|
118
|
+
|
|
119
|
+
@Inject(MetricsService)
|
|
120
|
+
class OrdersService {
|
|
121
|
+
private readonly ordersCreated: ReturnType<MetricsService['counter']>;
|
|
122
|
+
|
|
123
|
+
constructor(metrics: MetricsService) {
|
|
124
|
+
this.ordersCreated = metrics.counter({
|
|
125
|
+
name: 'orders_created_total',
|
|
126
|
+
help: 'Total orders created',
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
recordOrderCreated(): void {
|
|
131
|
+
this.ordersCreated.inc();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Calling `MetricsService.counter(...)` again with the same name recreates the collector and follows Prometheus' duplicate-name failure behavior. Store and reuse the collector instead of creating it inside each request or command handler.
|
|
137
|
+
|
|
138
|
+
`MetricsService.getRegistry()` returns the same active `prom-client` Registry used by the module scrape endpoint, built-in HTTP collectors, platform telemetry, and custom collectors created through the service. Prefer passing an explicit `registry` to `MetricsModule.forRoot({ registry })` when you own the bootstrap. Use `getRegistry()` for advanced integrations that receive `MetricsService` through DI and need to register a third-party Prometheus collector on the already active registry.
|
|
139
|
+
|
|
106
140
|
### Share one registry for framework and app metrics
|
|
107
141
|
|
|
108
142
|
```ts
|
|
143
|
+
import { Module } from '@fluojs/core';
|
|
109
144
|
import { Counter, Registry } from 'prom-client';
|
|
110
145
|
import { MetricsModule } from '@fluojs/metrics';
|
|
111
146
|
|
|
@@ -169,16 +204,18 @@ MetricsModule.forRoot({
|
|
|
169
204
|
## Public API
|
|
170
205
|
|
|
171
206
|
- `MetricsModule.forRoot(options)`
|
|
172
|
-
- `MetricsService`
|
|
207
|
+
- `MetricsService`, including `counter(...)`, `gauge(...)`, `histogram(...)`, and `getRegistry()`
|
|
173
208
|
- `METER_PROVIDER`
|
|
174
209
|
- `PrometheusMeterProvider`
|
|
210
|
+
- Meter abstraction types: `MeterProvider`, `MeterCounter`, `MeterGauge`, and `MeterHistogram`
|
|
175
211
|
- `HttpMetricsMiddleware` and HTTP path-label option types
|
|
176
212
|
- Module options including `provider` (currently only `'prometheus'`), module-level `middleware`, and endpoint-scoped `endpointMiddleware`
|
|
177
213
|
- `Registry` from `prom-client`
|
|
178
214
|
|
|
179
215
|
### Operational defaults
|
|
180
216
|
|
|
181
|
-
- `path` defaults to `'/metrics'`, and `path: false` disables the scrape endpoint entirely.
|
|
217
|
+
- `path` defaults to `'/metrics'`, any string path including `''` exposes a scrape endpoint, and `path: false` disables the scrape endpoint entirely.
|
|
218
|
+
- When `registry` is omitted, each application bootstrap owns a fresh isolated registry, `MetricsService`, meter provider, and telemetry collector set.
|
|
182
219
|
- The scrape response uses the active registry's Prometheus content type and registry contents.
|
|
183
220
|
- `defaultMetrics` defaults to `true`, and `defaultMetrics: false` disables Prometheus default process and Node.js collectors for that registry.
|
|
184
221
|
- `endpointMiddleware` binds class-based route-scoped middleware only to the scrape endpoint; with HTTP instrumentation enabled, endpoint middleware failures are counted by the built-in HTTP collectors.
|
package/dist/metrics-module.d.ts
CHANGED
|
@@ -57,5 +57,6 @@ export declare class MetricsModule {
|
|
|
57
57
|
* @returns A runtime module that exposes metrics through the configured path.
|
|
58
58
|
*/
|
|
59
59
|
static forRoot(options?: MetricsModuleOptions): ModuleType;
|
|
60
|
+
private static createRegistry;
|
|
60
61
|
}
|
|
61
62
|
//# sourceMappingURL=metrics-module.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metrics-module.d.ts","sourceRoot":"","sources":["../src/metrics-module.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"metrics-module.d.ts","sourceRoot":"","sources":["../src/metrics-module.ts"],"names":[],"mappings":"AAEA,OAAO,EAA8B,KAAK,UAAU,EAAE,KAAK,cAAc,EAAuB,MAAM,cAAc,CAAC;AACrH,OAAO,EAAgB,KAAK,UAAU,EAA8C,MAAM,iBAAiB,CAAC;AAC5G,OAAO,EAAgE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE1G,OAAO,EAGL,KAAK,wBAAwB,EAC7B,KAAK,8BAA8B,EACpC,MAAM,8BAA8B,CAAC;AAKtC,qFAAqF;AACrF,MAAM,WAAW,kBAAkB;IACjC,iGAAiG;IACjG,aAAa,CAAC,EAAE,wBAAwB,CAAC;IACzC,kFAAkF;IAClF,mBAAmB,CAAC,EAAE,8BAA8B,CAAC;IACrD,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2EAA2E;IAC3E,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,kGAAkG;IAClG,IAAI,CAAC,EAAE,OAAO,GAAG,kBAAkB,CAAC;IACpC,yHAAyH;IACzH,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,sHAAsH;IACtH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,mGAAmG;IACnG,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;IAC9B,uGAAuG;IACvG,kBAAkB,CAAC,EAAE,KAAK,CAAC,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,UAAU,CAAC,CAAC;IAC/D,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE;QAClB,iEAAiE;QACjE,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,iDAAiD;QACjD,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,iFAAiF;IACjF,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,sFAAsF;AACtF,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAC,oBAAoB,CAA2B;IAE9D;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,oBAAyB,GAAG,UAAU;IAqF9D,OAAO,CAAC,MAAM,CAAC,cAAc;CAU9B"}
|
package/dist/metrics-module.js
CHANGED
|
@@ -3,6 +3,7 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol"
|
|
|
3
3
|
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
4
4
|
function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
|
|
5
5
|
function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
|
|
6
|
+
import { Inject } from '@fluojs/core';
|
|
6
7
|
import { ContainerResolutionError } from '@fluojs/di';
|
|
7
8
|
import { Controller, Get, forRoutes } from '@fluojs/http';
|
|
8
9
|
import { defineModule, PLATFORM_SHELL } from '@fluojs/runtime';
|
|
@@ -43,24 +44,26 @@ export class MetricsModule {
|
|
|
43
44
|
}
|
|
44
45
|
const httpOptions = resolveHttpOptions(options.http);
|
|
45
46
|
const metricsPath = options.path === undefined ? '/metrics' : options.path;
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
const endpointMiddleware = metricsPath ? (options.endpointMiddleware ?? []).map(middlewareClass => forRoutes(middlewareClass, metricsPath)) : [];
|
|
57
|
-
const middleware = [...(httpOptions ? [new HttpMetricsMiddleware(registry, httpOptions)] : []), ...endpointMiddleware, ...(options.middleware ?? [])];
|
|
58
|
-
const providers = [{
|
|
47
|
+
const registryToken = Symbol('MetricsModule.registry');
|
|
48
|
+
const platformTelemetryToken = Symbol('MetricsModule.platformTelemetry');
|
|
49
|
+
const httpMetricsMiddleware = httpOptions ? createHttpMetricsMiddleware(registryToken, httpOptions) : undefined;
|
|
50
|
+
const endpointMiddleware = typeof metricsPath === 'string' ? (options.endpointMiddleware ?? []).map(middlewareClass => forRoutes(middlewareClass, metricsPath)) : [];
|
|
51
|
+
const middleware = [...(httpMetricsMiddleware ? [httpMetricsMiddleware] : []), ...endpointMiddleware, ...(options.middleware ?? [])];
|
|
52
|
+
const providers = [...(httpMetricsMiddleware ? [httpMetricsMiddleware] : []), {
|
|
53
|
+
provide: registryToken,
|
|
54
|
+
useFactory: () => MetricsModule.createRegistry(options)
|
|
55
|
+
}, {
|
|
59
56
|
provide: MetricsService,
|
|
60
|
-
|
|
57
|
+
inject: [registryToken],
|
|
58
|
+
useFactory: registry => new MetricsService(assertPrometheusRegistry(registry))
|
|
61
59
|
}, {
|
|
62
60
|
provide: METER_PROVIDER,
|
|
63
|
-
|
|
61
|
+
inject: [registryToken],
|
|
62
|
+
useFactory: registry => new PrometheusMeterProvider(assertPrometheusRegistry(registry))
|
|
63
|
+
}, {
|
|
64
|
+
provide: platformTelemetryToken,
|
|
65
|
+
inject: [registryToken],
|
|
66
|
+
useFactory: registry => new RuntimePlatformTelemetry(assertPrometheusRegistry(registry), options.registry ? 'shared' : 'isolated', options.platformTelemetry)
|
|
64
67
|
}];
|
|
65
68
|
const controllers = [];
|
|
66
69
|
if (typeof metricsPath === 'string') {
|
|
@@ -72,14 +75,16 @@ export class MetricsModule {
|
|
|
72
75
|
({
|
|
73
76
|
e: [_initProto],
|
|
74
77
|
c: [_MetricsController, _initClass]
|
|
75
|
-
} = _applyDecs(this, [Controller('')], [[Get(metricsRoutePath), 2, "getMetrics"]]));
|
|
78
|
+
} = _applyDecs(this, [Inject(registryToken, platformTelemetryToken), Controller('')], [[Get(metricsRoutePath), 2, "getMetrics"]]));
|
|
76
79
|
}
|
|
77
|
-
constructor() {
|
|
80
|
+
constructor(registry, platformTelemetry) {
|
|
81
|
+
this.registry = registry;
|
|
82
|
+
this.platformTelemetry = platformTelemetry;
|
|
78
83
|
_initProto(this);
|
|
79
84
|
}
|
|
80
85
|
async getMetrics(_input, ctx) {
|
|
81
|
-
ctx.response.setHeader('content-type', registry.contentType);
|
|
82
|
-
return platformTelemetry.collectMetrics(ctx, registry);
|
|
86
|
+
ctx.response.setHeader('content-type', this.registry.contentType);
|
|
87
|
+
return this.platformTelemetry.collectMetrics(ctx, this.registry);
|
|
83
88
|
}
|
|
84
89
|
static {
|
|
85
90
|
_initClass();
|
|
@@ -95,6 +100,16 @@ export class MetricsModule {
|
|
|
95
100
|
});
|
|
96
101
|
return MetricsRuntimeModule;
|
|
97
102
|
}
|
|
103
|
+
static createRegistry(options) {
|
|
104
|
+
const registry = options.registry ?? new PrometheusRegistry();
|
|
105
|
+
if (options.defaultMetrics !== false && !MetricsModule.registeredRegistries.has(registry)) {
|
|
106
|
+
MetricsModule.registeredRegistries.add(registry);
|
|
107
|
+
collectDefaultMetrics({
|
|
108
|
+
register: registry
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return registry;
|
|
112
|
+
}
|
|
98
113
|
}
|
|
99
114
|
const PLATFORM_COMPONENT_LABELS = ['component_id', 'component_kind', 'operation', 'result', 'env', 'instance'];
|
|
100
115
|
const REGISTRY_MODE_LABELS = ['mode'];
|
|
@@ -103,6 +118,32 @@ const HEALTH_STATUSES = ['healthy', 'unhealthy', 'degraded'];
|
|
|
103
118
|
const READINESS_STATUSES = ['ready', 'not-ready', 'degraded'];
|
|
104
119
|
const PLATFORM_SHELL_TOKEN_NAME = 'PLATFORM_SHELL';
|
|
105
120
|
const PLATFORM_SHELL_TOKEN_NAMES = new Set([PLATFORM_SHELL_TOKEN_NAME, String(PLATFORM_SHELL)]);
|
|
121
|
+
function createHttpMetricsMiddleware(registryToken, httpOptions) {
|
|
122
|
+
let _initClass2;
|
|
123
|
+
let _MetricsHttpMiddlewar;
|
|
124
|
+
class MetricsHttpMiddleware {
|
|
125
|
+
static {
|
|
126
|
+
[_MetricsHttpMiddlewar, _initClass2] = _applyDecs(this, [Inject(registryToken)], []).c;
|
|
127
|
+
}
|
|
128
|
+
delegate;
|
|
129
|
+
constructor(registry) {
|
|
130
|
+
this.delegate = new HttpMetricsMiddleware(registry, httpOptions);
|
|
131
|
+
}
|
|
132
|
+
handle(context, next) {
|
|
133
|
+
return this.delegate.handle(context, next);
|
|
134
|
+
}
|
|
135
|
+
static {
|
|
136
|
+
_initClass2();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return _MetricsHttpMiddlewar;
|
|
140
|
+
}
|
|
141
|
+
function assertPrometheusRegistry(value) {
|
|
142
|
+
if (!(value instanceof PrometheusRegistry)) {
|
|
143
|
+
throw new Error('MetricsModule registry provider resolved an invalid Prometheus registry.');
|
|
144
|
+
}
|
|
145
|
+
return value;
|
|
146
|
+
}
|
|
106
147
|
function toReadinessValue(status) {
|
|
107
148
|
return status === 'ready' ? 1 : 0;
|
|
108
149
|
}
|
|
@@ -299,17 +340,9 @@ function isMissingPlatformShellResolutionError(error) {
|
|
|
299
340
|
return false;
|
|
300
341
|
}
|
|
301
342
|
const containerError = error;
|
|
302
|
-
const
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
return message.startsWith(`No provider registered for token ${token}.`);
|
|
306
|
-
}
|
|
307
|
-
for (const tokenName of PLATFORM_SHELL_TOKEN_NAMES) {
|
|
308
|
-
if (message.startsWith(`No provider registered for token ${tokenName}.`)) {
|
|
309
|
-
return true;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
return false;
|
|
343
|
+
const token = typeof containerError.meta?.token === 'string' ? containerError.meta.token : undefined;
|
|
344
|
+
const hint = typeof containerError.meta?.hint === 'string' ? containerError.meta.hint : undefined;
|
|
345
|
+
return token !== undefined && PLATFORM_SHELL_TOKEN_NAMES.has(token) && hint === 'Ensure the provider is registered in a module\'s providers array, or that the module exporting it is imported by the consuming module.';
|
|
313
346
|
}
|
|
314
347
|
function resolveHttpOptions(http) {
|
|
315
348
|
if (!http) {
|
|
@@ -318,6 +351,9 @@ function resolveHttpOptions(http) {
|
|
|
318
351
|
if (http === true) {
|
|
319
352
|
return {};
|
|
320
353
|
}
|
|
354
|
+
if (http.pathLabelMode === 'raw' && http.allowUnsafeRawPathLabelMode !== true) {
|
|
355
|
+
throw new Error('HttpMetricsMiddleware pathLabelMode "raw" is disabled by default. Pass allowUnsafeRawPathLabelMode: true only when you have bounded path cardinality.');
|
|
356
|
+
}
|
|
321
357
|
return {
|
|
322
358
|
allowUnsafeRawPathLabelMode: http.allowUnsafeRawPathLabelMode,
|
|
323
359
|
pathLabelMode: http.pathLabelMode,
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"monitoring",
|
|
9
9
|
"observability"
|
|
10
10
|
],
|
|
11
|
-
"version": "1.0.
|
|
11
|
+
"version": "1.0.4",
|
|
12
12
|
"private": false,
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"repository": {
|
|
@@ -36,9 +36,10 @@
|
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"prom-client": "^15.1.3",
|
|
39
|
-
"@fluojs/
|
|
40
|
-
"@fluojs/http": "^1.1.
|
|
41
|
-
"@fluojs/
|
|
39
|
+
"@fluojs/core": "^1.0.3",
|
|
40
|
+
"@fluojs/http": "^1.1.2",
|
|
41
|
+
"@fluojs/di": "^1.1.0",
|
|
42
|
+
"@fluojs/runtime": "^1.1.8"
|
|
42
43
|
},
|
|
43
44
|
"devDependencies": {
|
|
44
45
|
"@types/node": "^22.0.0",
|