🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@fluojs/metrics

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@fluojs/metrics - npm Package Compare versions

Comparing version
1.0.0-beta.2
to
1.0.0-beta.3
+1
-1
dist/http-metrics-middleware.d.ts
import type { FrameworkRequest, Middleware, MiddlewareContext, Next } from '@fluojs/http';
import type { Registry } from 'prom-client';
import { type Registry } from 'prom-client';
/** Strategy used to label request paths in emitted HTTP metrics. */

@@ -4,0 +4,0 @@ export type HttpMetricsPathLabelMode = 'raw' | 'template';

@@ -1,1 +0,1 @@

{"version":3,"file":"http-metrics-middleware.d.ts","sourceRoot":"","sources":["../src/http-metrics-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAC1F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAkB5C,oEAAoE;AACpE,MAAM,MAAM,wBAAwB,GAAG,KAAK,GAAG,UAAU,CAAC;AAE1D,wDAAwD;AACxD,MAAM,WAAW,2BAA2B;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,gBAAgB,CAAC;CAC3B;AAED,2EAA2E;AAC3E,MAAM,MAAM,8BAA8B,GAAG,CAAC,OAAO,EAAE,2BAA2B,KAAK,MAAM,CAAC;AAE9F,8DAA8D;AAC9D,MAAM,WAAW,4BAA4B;IAC3C,aAAa,CAAC,EAAE,wBAAwB,CAAC;IACzC,mBAAmB,CAAC,EAAE,8BAA8B,CAAC;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAsBD;;GAEG;AACH,qBAAa,qBAAsB,YAAW,UAAU;IACtD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoB;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;IAChD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAsB;IACtD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA2B;IACzD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAiC;IACtE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;gBAE9B,QAAQ,EAAE,QAAQ,EAAE,OAAO,GAAE,4BAAiC;IA2B1E,OAAO,CAAC,gBAAgB;IAmBlB,MAAM,CAAC,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBnE,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,oBAAoB;CAsB7B"}
{"version":3,"file":"http-metrics-middleware.d.ts","sourceRoot":"","sources":["../src/http-metrics-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAC1F,OAAO,EAAsB,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAqBhE,oEAAoE;AACpE,MAAM,MAAM,wBAAwB,GAAG,KAAK,GAAG,UAAU,CAAC;AAE1D,wDAAwD;AACxD,MAAM,WAAW,2BAA2B;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,gBAAgB,CAAC;CAC3B;AAED,2EAA2E;AAC3E,MAAM,MAAM,8BAA8B,GAAG,CAAC,OAAO,EAAE,2BAA2B,KAAK,MAAM,CAAC;AAE9F,8DAA8D;AAC9D,MAAM,WAAW,4BAA4B;IAC3C,aAAa,CAAC,EAAE,wBAAwB,CAAC;IACzC,mBAAmB,CAAC,EAAE,8BAA8B,CAAC;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAsBD;;GAEG;AACH,qBAAa,qBAAsB,YAAW,UAAU;IACtD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoB;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;IAChD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAsB;IACtD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA2B;IACzD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAiC;IACtE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;gBAE9B,QAAQ,EAAE,QAAQ,EAAE,OAAO,GAAE,4BAAiC;IA2B1E,OAAO,CAAC,gBAAgB;IAmBlB,MAAM,CAAC,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBnE,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,oBAAoB;CAsB7B"}

@@ -0,2 +1,5 @@

import { Counter, Histogram } from 'prom-client';
import { createPrometheusCounter, createPrometheusHistogram } from './providers/prometheus-metrics-factory.js';
const FRAMEWORK_HTTP_COUNTERS = new WeakSet();
const FRAMEWORK_HTTP_HISTOGRAMS = new WeakSet();

@@ -44,3 +47,3 @@ /** Strategy used to label request paths in emitted HTTP metrics. */

this.unknownPathLabel = options.unknownPathLabel ?? 'UNKNOWN';
this.requestsTotal = createPrometheusCounter(registry, {
this.requestsTotal = getOrCreateHttpCounter(registry, {
help: 'Total number of HTTP requests',

@@ -50,3 +53,3 @@ labelNames: ['method', 'path', 'status'],

});
this.errorsTotal = createPrometheusCounter(registry, {
this.errorsTotal = getOrCreateHttpCounter(registry, {
help: 'Total number of HTTP error responses (4xx/5xx)',

@@ -56,3 +59,3 @@ labelNames: ['method', 'path', 'status'],

});
this.requestDuration = createPrometheusHistogram(registry, {
this.requestDuration = getOrCreateHttpHistogram(registry, {
help: 'HTTP request duration in seconds',

@@ -122,2 +125,34 @@ labelNames: ['method', 'path', 'status'],

}
function getOrCreateHttpCounter(registry, config) {
const existing = registry.getSingleMetric(config.name);
if (existing instanceof Counter) {
if (!FRAMEWORK_HTTP_COUNTERS.has(existing)) {
throw new Error(`Metric name "${config.name}" is already registered by the application. Built-in HTTP metrics require framework-owned collectors.`);
}
return existing;
}
const counter = createPrometheusCounter(registry, {
help: config.help,
labelNames: [...config.labelNames],
name: config.name
});
FRAMEWORK_HTTP_COUNTERS.add(counter);
return counter;
}
function getOrCreateHttpHistogram(registry, config) {
const existing = registry.getSingleMetric(config.name);
if (existing instanceof Histogram) {
if (!FRAMEWORK_HTTP_HISTOGRAMS.has(existing)) {
throw new Error(`Metric name "${config.name}" is already registered by the application. Built-in HTTP metrics require framework-owned collectors.`);
}
return existing;
}
const histogram = createPrometheusHistogram(registry, {
help: config.help,
labelNames: [...config.labelNames],
name: config.name
});
FRAMEWORK_HTTP_HISTOGRAMS.add(histogram);
return histogram;
}
function normalizePathToTemplate(path, params) {

@@ -124,0 +159,0 @@ if (!path) {

@@ -0,1 +1,9 @@

/**
* Prometheus registry constructor re-exported for applications that want custom
* application metrics and Fluo framework metrics to share one scrape endpoint.
*
* @remarks
* The implementation is `prom-client`'s `Registry`; duplicate metric names still
* follow Prometheus registry semantics and fail fast.
*/
export { Registry } from 'prom-client';

@@ -2,0 +10,0 @@ export * from './metrics-module.js';

@@ -1,1 +0,1 @@

{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,0CAA0C,CAAC;AACzD,cAAc,8BAA8B,CAAC"}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,0CAA0C,CAAC;AACzD,cAAc,8BAA8B,CAAC"}

@@ -0,1 +1,9 @@

/**
* Prometheus registry constructor re-exported for applications that want custom
* application metrics and Fluo framework metrics to share one scrape endpoint.
*
* @remarks
* The implementation is `prom-client`'s `Registry`; duplicate metric names still
* follow Prometheus registry semantics and fail fast.
*/
export { Registry } from 'prom-client';

@@ -2,0 +10,0 @@ export * from './metrics-module.js';

@@ -288,8 +288,9 @@ function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 !== (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }

const containerError = error;
const message = String(containerError.message ?? '');
const token = typeof containerError.meta?.['token'] === 'string' ? containerError.meta['token'] : undefined;
if (token && PLATFORM_SHELL_TOKEN_NAMES.has(token)) {
return containerError.message.startsWith(`No provider registered for token ${token}.`);
return message.startsWith(`No provider registered for token ${token}.`);
}
for (const tokenName of PLATFORM_SHELL_TOKEN_NAMES) {
if (containerError.message.startsWith(`No provider registered for token ${tokenName}.`)) {
if (message.startsWith(`No provider registered for token ${tokenName}.`)) {
return true;

@@ -296,0 +297,0 @@ }

@@ -11,3 +11,3 @@ {

],
"version": "1.0.0-beta.2",
"version": "1.0.0-beta.3",
"private": false,

@@ -40,5 +40,5 @@ "license": "MIT",

"prom-client": "^15.1.3",
"@fluojs/di": "^1.0.0-beta.2",
"@fluojs/http": "^1.0.0-beta.1",
"@fluojs/runtime": "^1.0.0-beta.2"
"@fluojs/di": "^1.0.0-beta.6",
"@fluojs/http": "^1.0.0-beta.9",
"@fluojs/runtime": "^1.0.0-beta.9"
},

@@ -45,0 +45,0 @@ "devDependencies": {

@@ -31,3 +31,3 @@ # @fluojs/metrics

루트 모듈에 `MetricsModule.forRoot()`를 추가하여 기본 `/metrics` 엔드포인트를 활성화합니다.
루트 모듈에 `MetricsModule.forRoot()`를 추가하여 기본 `/metrics` 엔드포인트를 활성화합니다. HTTP 요청 수와 지연 시간까지 계측하려면 `http: true` 또는 `http` 옵션 객체를 함께 전달합니다.

@@ -40,3 +40,3 @@ ```typescript

imports: [
MetricsModule.forRoot(),
MetricsModule.forRoot({ http: true }),
],

@@ -49,3 +49,3 @@ })

`MetricsModule.forRoot()`는 기본적으로 `GET /metrics`를 노출합니다. 운영 환경에서는 이 경계를 명시적으로 다루세요. 플랫폼 프록시/네트워크 제어를 붙이기 전까지 `path: false`로 비활성화하거나, 전용 endpoint middleware를 연결하는 방식을 권장합니다.
`MetricsModule.forRoot()`는 기본적으로 `GET /metrics`를 노출합니다. `http: true`를 전달한 경우에만 HTTP 요청 계측 미들웨어가 설치됩니다. 운영 환경에서는 이 경계를 명시적으로 다루세요. 플랫폼 프록시/네트워크 제어를 붙이기 전까지 `path: false`로 비활성화하거나, 전용 endpoint middleware를 연결하는 방식을 권장합니다.

@@ -122,3 +122,3 @@ ## 공통 패턴

imports: [
MetricsModule.forRoot({ registry: sharedRegistry }),
MetricsModule.forRoot({ http: true, registry: sharedRegistry }),
],

@@ -129,2 +129,8 @@ })

여러 `MetricsModule` 인스턴스가 같은 Registry를 의도적으로 공유하는 경우, 내장 HTTP 메트릭은 기존 `http_requests_total`, `http_errors_total`, `http_request_duration_seconds` collector를 재사용합니다. 애플리케이션이 직접 등록한 중복 메트릭 이름은 Prometheus Registry 규칙대로 계속 빠르게 실패합니다.
### 중복 메트릭 이름은 계속 빠르게 실패합니다
Prometheus 메트릭 이름은 하나의 Registry 안에서 고유해야 합니다. 공유 Registry 모드는 애플리케이션 메트릭의 중복 이름을 조용히 덮어쓰지 않고 이 동작을 유지합니다.
### 런타임 플랫폼 텔레메트리

@@ -180,3 +186,4 @@

- `endpointMiddleware`는 스크레이프 엔드포인트에만 route-scoped middleware를 바인딩합니다.
- HTTP 메트릭은 기본적으로 템플릿 기반 경로 라벨 정규화를 사용합니다.
- HTTP 메트릭은 `http: true` 또는 `http` 옵션 객체를 전달한 경우에만 설치되며, 설치된 뒤에는 기본적으로 템플릿 기반 경로 라벨 정규화를 사용합니다.
- 내장 HTTP collector는 같은 Registry를 공유하는 모듈 인스턴스 사이에서 재사용되며, 커스텀 애플리케이션 메트릭 이름 충돌은 Prometheus의 중복 이름 실패 동작을 유지합니다.
- raw path 라벨은 `allowUnsafeRawPathLabelMode: true`를 명시한 bounded internal route에서만 사용해야 합니다.

@@ -183,0 +190,0 @@ - 플랫폼 텔레메트리는 `PLATFORM_SHELL`이 실제로 누락된 경우에만 생략되며, 그 외 resolve 실패는 스크레이프를 실패시킵니다.

@@ -36,3 +36,3 @@ # @fluojs/metrics

@Module({
imports: [MetricsModule.forRoot()],
imports: [MetricsModule.forRoot({ http: true })],
})

@@ -42,3 +42,3 @@ class AppModule {}

`MetricsModule.forRoot()` still exposes `GET /metrics` by default. For production deployments, make that endpoint boundary explicit: either disable it with `path: false` until a platform-level proxy is in place, or attach dedicated endpoint middleware.
`MetricsModule.forRoot()` still exposes `GET /metrics` by default. Pass `http: true` (or an `http` options object) when you want the module to install HTTP request instrumentation middleware. 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.

@@ -99,3 +99,3 @@ ## Common Patterns

@Module({
imports: [MetricsModule.forRoot({ registry })],
imports: [MetricsModule.forRoot({ http: true, registry })],
})

@@ -105,2 +105,4 @@ class AppModule {}

When multiple metrics module instances intentionally share the same registry, built-in HTTP metrics reuse the existing `http_requests_total`, `http_errors_total`, and `http_request_duration_seconds` collectors instead of registering duplicate framework metrics. Application-defined duplicate names still fail fast.
### Duplicate metric names still fail fast

@@ -160,3 +162,4 @@

- `endpointMiddleware` binds route-scoped middleware only to the scrape endpoint.
- HTTP metrics default to template-normalized path labels.
- HTTP metrics are installed only when `http: true` or an `http` options object is provided, and then default to template-normalized path labels.
- Built-in HTTP collectors are reused when module instances share one registry; custom application metric name collisions keep Prometheus' duplicate-name failure behavior.
- Raw path labels require `allowUnsafeRawPathLabelMode: true` and should stay limited to bounded internal routes.

@@ -163,0 +166,0 @@ - Platform telemetry is omitted only when `PLATFORM_SHELL` is genuinely missing; other resolution failures fail the scrape.