@spinajs/telemetry 2.0.482
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.md +482 -0
- package/lib/cjs/InMemoryPerfSink.d.ts +51 -0
- package/lib/cjs/InMemoryPerfSink.d.ts.map +1 -0
- package/lib/cjs/InMemoryPerfSink.js +100 -0
- package/lib/cjs/InMemoryPerfSink.js.map +1 -0
- package/lib/cjs/PromMetricSink.d.ts +26 -0
- package/lib/cjs/PromMetricSink.d.ts.map +1 -0
- package/lib/cjs/PromMetricSink.js +68 -0
- package/lib/cjs/PromMetricSink.js.map +1 -0
- package/lib/cjs/bootstrap.d.ts +13 -0
- package/lib/cjs/bootstrap.d.ts.map +1 -0
- package/lib/cjs/bootstrap.js +57 -0
- package/lib/cjs/bootstrap.js.map +1 -0
- package/lib/cjs/config/telemetry.d.ts +63 -0
- package/lib/cjs/config/telemetry.d.ts.map +1 -0
- package/lib/cjs/config/telemetry.js +78 -0
- package/lib/cjs/config/telemetry.js.map +1 -0
- package/lib/cjs/controllers/Metrics.d.ts +26 -0
- package/lib/cjs/controllers/Metrics.d.ts.map +1 -0
- package/lib/cjs/controllers/Metrics.js +63 -0
- package/lib/cjs/controllers/Metrics.js.map +1 -0
- package/lib/cjs/controllers/Telemetry.d.ts +92 -0
- package/lib/cjs/controllers/Telemetry.d.ts.map +1 -0
- package/lib/cjs/controllers/Telemetry.js +226 -0
- package/lib/cjs/controllers/Telemetry.js.map +1 -0
- package/lib/cjs/dto/index.d.ts +21 -0
- package/lib/cjs/dto/index.d.ts.map +1 -0
- package/lib/cjs/dto/index.js +64 -0
- package/lib/cjs/dto/index.js.map +1 -0
- package/lib/cjs/endpoints.d.ts +32 -0
- package/lib/cjs/endpoints.d.ts.map +1 -0
- package/lib/cjs/endpoints.js +38 -0
- package/lib/cjs/endpoints.js.map +1 -0
- package/lib/cjs/health.d.ts +55 -0
- package/lib/cjs/health.d.ts.map +1 -0
- package/lib/cjs/health.js +111 -0
- package/lib/cjs/health.js.map +1 -0
- package/lib/cjs/index.d.ts +16 -0
- package/lib/cjs/index.d.ts.map +1 -0
- package/lib/cjs/index.js +32 -0
- package/lib/cjs/index.js.map +1 -0
- package/lib/cjs/metrics.d.ts +16 -0
- package/lib/cjs/metrics.d.ts.map +1 -0
- package/lib/cjs/metrics.js +32 -0
- package/lib/cjs/metrics.js.map +1 -0
- package/lib/cjs/middleware.d.ts +81 -0
- package/lib/cjs/middleware.d.ts.map +1 -0
- package/lib/cjs/middleware.js +210 -0
- package/lib/cjs/middleware.js.map +1 -0
- package/lib/cjs/package.json +1 -0
- package/lib/cjs/policies/PublicPolicy.d.ts +16 -0
- package/lib/cjs/policies/PublicPolicy.d.ts.map +1 -0
- package/lib/cjs/policies/PublicPolicy.js +34 -0
- package/lib/cjs/policies/PublicPolicy.js.map +1 -0
- package/lib/cjs/policies/TelemetryTokenPolicy.d.ts +19 -0
- package/lib/cjs/policies/TelemetryTokenPolicy.d.ts.map +1 -0
- package/lib/cjs/policies/TelemetryTokenPolicy.js +65 -0
- package/lib/cjs/policies/TelemetryTokenPolicy.js.map +1 -0
- package/lib/cjs/requestStats.d.ts +81 -0
- package/lib/cjs/requestStats.d.ts.map +1 -0
- package/lib/cjs/requestStats.js +134 -0
- package/lib/cjs/requestStats.js.map +1 -0
- package/lib/cjs/responses.d.ts +23 -0
- package/lib/cjs/responses.d.ts.map +1 -0
- package/lib/cjs/responses.js +35 -0
- package/lib/cjs/responses.js.map +1 -0
- package/lib/cjs/routeStats.d.ts +43 -0
- package/lib/cjs/routeStats.d.ts.map +1 -0
- package/lib/cjs/routeStats.js +59 -0
- package/lib/cjs/routeStats.js.map +1 -0
- package/lib/cjs/schemas/HealthResponse.schema.d.ts +30 -0
- package/lib/cjs/schemas/HealthResponse.schema.d.ts.map +1 -0
- package/lib/cjs/schemas/HealthResponse.schema.js +15 -0
- package/lib/cjs/schemas/HealthResponse.schema.js.map +1 -0
- package/lib/cjs/schemas/PerfResponse.schema.d.ts +56 -0
- package/lib/cjs/schemas/PerfResponse.schema.d.ts.map +1 -0
- package/lib/cjs/schemas/PerfResponse.schema.js +36 -0
- package/lib/cjs/schemas/PerfResponse.schema.js.map +1 -0
- package/lib/cjs/schemas/ReadyResponse.schema.d.ts +36 -0
- package/lib/cjs/schemas/ReadyResponse.schema.d.ts.map +1 -0
- package/lib/cjs/schemas/ReadyResponse.schema.js +23 -0
- package/lib/cjs/schemas/ReadyResponse.schema.js.map +1 -0
- package/lib/cjs/schemas/RequestStatsSnapshot.schema.d.ts +79 -0
- package/lib/cjs/schemas/RequestStatsSnapshot.schema.d.ts.map +1 -0
- package/lib/cjs/schemas/RequestStatsSnapshot.schema.js +30 -0
- package/lib/cjs/schemas/RequestStatsSnapshot.schema.js.map +1 -0
- package/lib/cjs/schemas/RoutesResponse.schema.d.ts +100 -0
- package/lib/cjs/schemas/RoutesResponse.schema.d.ts.map +1 -0
- package/lib/cjs/schemas/RoutesResponse.schema.js +25 -0
- package/lib/cjs/schemas/RoutesResponse.schema.js.map +1 -0
- package/lib/cjs/schemas/StatsResponse.schema.d.ts +158 -0
- package/lib/cjs/schemas/StatsResponse.schema.d.ts.map +1 -0
- package/lib/cjs/schemas/StatsResponse.schema.js +19 -0
- package/lib/cjs/schemas/StatsResponse.schema.js.map +1 -0
- package/lib/cjs/schemas/TimelineResponse.schema.d.ts +109 -0
- package/lib/cjs/schemas/TimelineResponse.schema.d.ts.map +1 -0
- package/lib/cjs/schemas/TimelineResponse.schema.js +28 -0
- package/lib/cjs/schemas/TimelineResponse.schema.js.map +1 -0
- package/lib/cjs/store.d.ts +58 -0
- package/lib/cjs/store.d.ts.map +1 -0
- package/lib/cjs/store.js +108 -0
- package/lib/cjs/store.js.map +1 -0
- package/lib/cjs/timeline.d.ts +48 -0
- package/lib/cjs/timeline.d.ts.map +1 -0
- package/lib/cjs/timeline.js +70 -0
- package/lib/cjs/timeline.js.map +1 -0
- package/lib/mjs/InMemoryPerfSink.d.ts +51 -0
- package/lib/mjs/InMemoryPerfSink.d.ts.map +1 -0
- package/lib/mjs/InMemoryPerfSink.js +97 -0
- package/lib/mjs/InMemoryPerfSink.js.map +1 -0
- package/lib/mjs/PromMetricSink.d.ts +26 -0
- package/lib/mjs/PromMetricSink.d.ts.map +1 -0
- package/lib/mjs/PromMetricSink.js +65 -0
- package/lib/mjs/PromMetricSink.js.map +1 -0
- package/lib/mjs/bootstrap.d.ts +13 -0
- package/lib/mjs/bootstrap.d.ts.map +1 -0
- package/lib/mjs/bootstrap.js +54 -0
- package/lib/mjs/bootstrap.js.map +1 -0
- package/lib/mjs/config/telemetry.d.ts +63 -0
- package/lib/mjs/config/telemetry.d.ts.map +1 -0
- package/lib/mjs/config/telemetry.js +76 -0
- package/lib/mjs/config/telemetry.js.map +1 -0
- package/lib/mjs/controllers/Metrics.d.ts +26 -0
- package/lib/mjs/controllers/Metrics.d.ts.map +1 -0
- package/lib/mjs/controllers/Metrics.js +60 -0
- package/lib/mjs/controllers/Metrics.js.map +1 -0
- package/lib/mjs/controllers/Telemetry.d.ts +92 -0
- package/lib/mjs/controllers/Telemetry.d.ts.map +1 -0
- package/lib/mjs/controllers/Telemetry.js +223 -0
- package/lib/mjs/controllers/Telemetry.js.map +1 -0
- package/lib/mjs/dto/index.d.ts +21 -0
- package/lib/mjs/dto/index.d.ts.map +1 -0
- package/lib/mjs/dto/index.js +58 -0
- package/lib/mjs/dto/index.js.map +1 -0
- package/lib/mjs/endpoints.d.ts +32 -0
- package/lib/mjs/endpoints.d.ts.map +1 -0
- package/lib/mjs/endpoints.js +34 -0
- package/lib/mjs/endpoints.js.map +1 -0
- package/lib/mjs/health.d.ts +55 -0
- package/lib/mjs/health.d.ts.map +1 -0
- package/lib/mjs/health.js +107 -0
- package/lib/mjs/health.js.map +1 -0
- package/lib/mjs/index.d.ts +16 -0
- package/lib/mjs/index.d.ts.map +1 -0
- package/lib/mjs/index.js +16 -0
- package/lib/mjs/index.js.map +1 -0
- package/lib/mjs/metrics.d.ts +16 -0
- package/lib/mjs/metrics.d.ts.map +1 -0
- package/lib/mjs/metrics.js +16 -0
- package/lib/mjs/metrics.js.map +1 -0
- package/lib/mjs/middleware.d.ts +81 -0
- package/lib/mjs/middleware.d.ts.map +1 -0
- package/lib/mjs/middleware.js +207 -0
- package/lib/mjs/middleware.js.map +1 -0
- package/lib/mjs/package.json +1 -0
- package/lib/mjs/policies/PublicPolicy.d.ts +16 -0
- package/lib/mjs/policies/PublicPolicy.d.ts.map +1 -0
- package/lib/mjs/policies/PublicPolicy.js +31 -0
- package/lib/mjs/policies/PublicPolicy.js.map +1 -0
- package/lib/mjs/policies/TelemetryTokenPolicy.d.ts +19 -0
- package/lib/mjs/policies/TelemetryTokenPolicy.d.ts.map +1 -0
- package/lib/mjs/policies/TelemetryTokenPolicy.js +62 -0
- package/lib/mjs/policies/TelemetryTokenPolicy.js.map +1 -0
- package/lib/mjs/requestStats.d.ts +81 -0
- package/lib/mjs/requestStats.d.ts.map +1 -0
- package/lib/mjs/requestStats.js +130 -0
- package/lib/mjs/requestStats.js.map +1 -0
- package/lib/mjs/responses.d.ts +23 -0
- package/lib/mjs/responses.d.ts.map +1 -0
- package/lib/mjs/responses.js +30 -0
- package/lib/mjs/responses.js.map +1 -0
- package/lib/mjs/routeStats.d.ts +43 -0
- package/lib/mjs/routeStats.d.ts.map +1 -0
- package/lib/mjs/routeStats.js +55 -0
- package/lib/mjs/routeStats.js.map +1 -0
- package/lib/mjs/schemas/HealthResponse.schema.d.ts +30 -0
- package/lib/mjs/schemas/HealthResponse.schema.d.ts.map +1 -0
- package/lib/mjs/schemas/HealthResponse.schema.js +13 -0
- package/lib/mjs/schemas/HealthResponse.schema.js.map +1 -0
- package/lib/mjs/schemas/PerfResponse.schema.d.ts +56 -0
- package/lib/mjs/schemas/PerfResponse.schema.d.ts.map +1 -0
- package/lib/mjs/schemas/PerfResponse.schema.js +34 -0
- package/lib/mjs/schemas/PerfResponse.schema.js.map +1 -0
- package/lib/mjs/schemas/ReadyResponse.schema.d.ts +36 -0
- package/lib/mjs/schemas/ReadyResponse.schema.d.ts.map +1 -0
- package/lib/mjs/schemas/ReadyResponse.schema.js +21 -0
- package/lib/mjs/schemas/ReadyResponse.schema.js.map +1 -0
- package/lib/mjs/schemas/RequestStatsSnapshot.schema.d.ts +79 -0
- package/lib/mjs/schemas/RequestStatsSnapshot.schema.d.ts.map +1 -0
- package/lib/mjs/schemas/RequestStatsSnapshot.schema.js +28 -0
- package/lib/mjs/schemas/RequestStatsSnapshot.schema.js.map +1 -0
- package/lib/mjs/schemas/RoutesResponse.schema.d.ts +100 -0
- package/lib/mjs/schemas/RoutesResponse.schema.d.ts.map +1 -0
- package/lib/mjs/schemas/RoutesResponse.schema.js +20 -0
- package/lib/mjs/schemas/RoutesResponse.schema.js.map +1 -0
- package/lib/mjs/schemas/StatsResponse.schema.d.ts +158 -0
- package/lib/mjs/schemas/StatsResponse.schema.d.ts.map +1 -0
- package/lib/mjs/schemas/StatsResponse.schema.js +14 -0
- package/lib/mjs/schemas/StatsResponse.schema.js.map +1 -0
- package/lib/mjs/schemas/TimelineResponse.schema.d.ts +109 -0
- package/lib/mjs/schemas/TimelineResponse.schema.d.ts.map +1 -0
- package/lib/mjs/schemas/TimelineResponse.schema.js +23 -0
- package/lib/mjs/schemas/TimelineResponse.schema.js.map +1 -0
- package/lib/mjs/store.d.ts +58 -0
- package/lib/mjs/store.d.ts.map +1 -0
- package/lib/mjs/store.js +105 -0
- package/lib/mjs/store.js.map +1 -0
- package/lib/mjs/timeline.d.ts +48 -0
- package/lib/mjs/timeline.d.ts.map +1 -0
- package/lib/mjs/timeline.js +66 -0
- package/lib/mjs/timeline.js.map +1 -0
- package/lib/tsconfig.cjs.tsbuildinfo +1 -0
- package/lib/tsconfig.mjs.tsbuildinfo +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { AsyncService, Autoinject, Injectable, Singleton } from '@spinajs/di';
|
|
11
|
+
import { Config } from '@spinajs/configuration';
|
|
12
|
+
/**
|
|
13
|
+
* A readiness probe for one dependency.
|
|
14
|
+
*
|
|
15
|
+
* Register a concrete subclass with `@Injectable( HealthCheck )` and it is
|
|
16
|
+
* discovered by {@link HealthCheckRunner} via `Array.ofType( HealthCheck )`.
|
|
17
|
+
* `@spinajs/telemetry` deliberately ships NO concrete checks — a database check
|
|
18
|
+
* belongs where the database dependency already is, not in the observability
|
|
19
|
+
* package.
|
|
20
|
+
*
|
|
21
|
+
* `check()` may throw; a throw counts as `down`.
|
|
22
|
+
*/
|
|
23
|
+
export class HealthCheck extends AsyncService {
|
|
24
|
+
}
|
|
25
|
+
const RANK = { up: 0, degraded: 1, down: 2 };
|
|
26
|
+
/**
|
|
27
|
+
* Severity of one check's status, for the reduction in {@link HealthCheckRunner.run}.
|
|
28
|
+
*
|
|
29
|
+
* An unrecognised value ranks as `down`, not as harmless: `HealthStatus` is only
|
|
30
|
+
* enforced at compile time, so a JS consumer ( or a check that forwards a status
|
|
31
|
+
* from somewhere else ) can return `'DOWN'` or `'UNHEALTHY'`. A plain
|
|
32
|
+
* `RANK[status] > RANK[worst]` reads `undefined` for those, and `undefined > n`
|
|
33
|
+
* is false, so a genuinely failing dependency would leave the overall status at
|
|
34
|
+
* `up` and `/telemetry/ready` would answer 200.
|
|
35
|
+
*/
|
|
36
|
+
function rank(status) {
|
|
37
|
+
return RANK[status] ?? RANK.down;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Runs every registered {@link HealthCheck} concurrently, each raced against a
|
|
41
|
+
* per-check timeout, and reduces the results to one overall status.
|
|
42
|
+
*
|
|
43
|
+
* The timeout is the point of the class: a readiness endpoint that can hang is
|
|
44
|
+
* worse than no readiness endpoint, because a kubelet probe blocks on it.
|
|
45
|
+
*/
|
|
46
|
+
let HealthCheckRunner = class HealthCheckRunner {
|
|
47
|
+
/**
|
|
48
|
+
* True when the overall status should be served as HTTP 503.
|
|
49
|
+
*/
|
|
50
|
+
isFailing(status) {
|
|
51
|
+
if (status === 'down')
|
|
52
|
+
return true;
|
|
53
|
+
return status === 'degraded' && this.FailOnDegraded === true;
|
|
54
|
+
}
|
|
55
|
+
async run() {
|
|
56
|
+
const checks = await Promise.all((this.Checks ?? []).map((c) => this.runOne(c)));
|
|
57
|
+
let worst = 'up';
|
|
58
|
+
for (const c of checks) {
|
|
59
|
+
// The unrecognised status is reduced AS `down`, but the check's own line in
|
|
60
|
+
// the report keeps the string the check actually returned — that value is
|
|
61
|
+
// the only clue an operator has that a check is misbehaving, and rewriting
|
|
62
|
+
// it would hide the misconfiguration behind a correct-looking report.
|
|
63
|
+
if (rank(c.status) > rank(worst))
|
|
64
|
+
worst = RANK[c.status] !== undefined ? c.status : 'down';
|
|
65
|
+
}
|
|
66
|
+
return { status: worst, checks };
|
|
67
|
+
}
|
|
68
|
+
async runOne(check) {
|
|
69
|
+
const startedAt = Date.now();
|
|
70
|
+
const timeoutMs = this.TimeoutMs ?? 2000;
|
|
71
|
+
let timer;
|
|
72
|
+
try {
|
|
73
|
+
const result = await Promise.race([
|
|
74
|
+
check.check(),
|
|
75
|
+
new Promise((_, reject) => {
|
|
76
|
+
timer = setTimeout(() => reject(new Error(`health check timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
77
|
+
}),
|
|
78
|
+
]);
|
|
79
|
+
return { name: check.Name, status: result.status, message: result.message, data: result.data, durationMs: Date.now() - startedAt };
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
return { name: check.Name, status: 'down', message: err?.message ?? String(err), durationMs: Date.now() - startedAt };
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
if (timer)
|
|
86
|
+
clearTimeout(timer);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
__decorate([
|
|
91
|
+
Autoinject(HealthCheck),
|
|
92
|
+
__metadata("design:type", Array)
|
|
93
|
+
], HealthCheckRunner.prototype, "Checks", void 0);
|
|
94
|
+
__decorate([
|
|
95
|
+
Config('telemetry.health.timeoutMs', { defaultValue: 2000 }),
|
|
96
|
+
__metadata("design:type", Number)
|
|
97
|
+
], HealthCheckRunner.prototype, "TimeoutMs", void 0);
|
|
98
|
+
__decorate([
|
|
99
|
+
Config('telemetry.health.failOnDegraded', { defaultValue: false }),
|
|
100
|
+
__metadata("design:type", Boolean)
|
|
101
|
+
], HealthCheckRunner.prototype, "FailOnDegraded", void 0);
|
|
102
|
+
HealthCheckRunner = __decorate([
|
|
103
|
+
Singleton(),
|
|
104
|
+
Injectable()
|
|
105
|
+
], HealthCheckRunner);
|
|
106
|
+
export { HealthCheckRunner };
|
|
107
|
+
//# sourceMappingURL=health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/health.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export * from './metrics.js';
|
|
2
|
+
export * from './bootstrap.js';
|
|
3
|
+
export * from './InMemoryPerfSink.js';
|
|
4
|
+
export * from './requestStats.js';
|
|
5
|
+
export * from './timeline.js';
|
|
6
|
+
export * from './routeStats.js';
|
|
7
|
+
export * from './store.js';
|
|
8
|
+
export * from './middleware.js';
|
|
9
|
+
export * from './endpoints.js';
|
|
10
|
+
export * from './PromMetricSink.js';
|
|
11
|
+
export * from './policies/TelemetryTokenPolicy.js';
|
|
12
|
+
export * from './policies/PublicPolicy.js';
|
|
13
|
+
export * from './responses.js';
|
|
14
|
+
export * from './health.js';
|
|
15
|
+
export * from './dto/index.js';
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,oCAAoC,CAAC;AACnD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC"}
|
package/lib/mjs/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export * from './metrics.js';
|
|
2
|
+
export * from './bootstrap.js';
|
|
3
|
+
export * from './InMemoryPerfSink.js';
|
|
4
|
+
export * from './requestStats.js';
|
|
5
|
+
export * from './timeline.js';
|
|
6
|
+
export * from './routeStats.js';
|
|
7
|
+
export * from './store.js';
|
|
8
|
+
export * from './middleware.js';
|
|
9
|
+
export * from './endpoints.js';
|
|
10
|
+
export * from './PromMetricSink.js';
|
|
11
|
+
export * from './policies/TelemetryTokenPolicy.js';
|
|
12
|
+
export * from './policies/PublicPolicy.js';
|
|
13
|
+
export * from './responses.js';
|
|
14
|
+
export * from './health.js';
|
|
15
|
+
export * from './dto/index.js';
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,oCAAoC,CAAC;AACnD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The metric abstraction lives in `@spinajs/telemetry-common`, which depends on nothing but
|
|
3
|
+
* `@spinajs/di` and prom-client.
|
|
4
|
+
*
|
|
5
|
+
* It was extracted for one reason: `@spinajs/orm` publishes connection-pool telemetry, and this
|
|
6
|
+
* package depends on `@spinajs/http`, `@spinajs/log`, `@spinajs/validation` and
|
|
7
|
+
* `@spinajs/configuration`. An ORM that depended on `@spinajs/telemetry` would put the whole HTTP
|
|
8
|
+
* stack underneath every database connection. Same reason `@spinajs/configuration-common` and
|
|
9
|
+
* `@spinajs/log-common` exist.
|
|
10
|
+
*
|
|
11
|
+
* Re-exported here so `import { Metrics } from '@spinajs/telemetry'` keeps working, and so both
|
|
12
|
+
* packages share ONE `Metrics` class identity — DI resolves the singleton by that identity, and a
|
|
13
|
+
* second copy would mean a second, separate registry.
|
|
14
|
+
*/
|
|
15
|
+
export * from '@spinajs/telemetry-common';
|
|
16
|
+
//# sourceMappingURL=metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/metrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,cAAc,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The metric abstraction lives in `@spinajs/telemetry-common`, which depends on nothing but
|
|
3
|
+
* `@spinajs/di` and prom-client.
|
|
4
|
+
*
|
|
5
|
+
* It was extracted for one reason: `@spinajs/orm` publishes connection-pool telemetry, and this
|
|
6
|
+
* package depends on `@spinajs/http`, `@spinajs/log`, `@spinajs/validation` and
|
|
7
|
+
* `@spinajs/configuration`. An ORM that depended on `@spinajs/telemetry` would put the whole HTTP
|
|
8
|
+
* stack underneath every database connection. Same reason `@spinajs/configuration-common` and
|
|
9
|
+
* `@spinajs/log-common` exist.
|
|
10
|
+
*
|
|
11
|
+
* Re-exported here so `import { Metrics } from '@spinajs/telemetry'` keeps working, and so both
|
|
12
|
+
* packages share ONE `Metrics` class identity — DI resolves the singleton by that identity, and a
|
|
13
|
+
* second copy would mean a second, separate registry.
|
|
14
|
+
*/
|
|
15
|
+
export * from '@spinajs/telemetry-common';
|
|
16
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../src/metrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,cAAc,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as express from 'express';
|
|
2
|
+
import { Log } from '@spinajs/log';
|
|
3
|
+
import { ServerMiddleware, Request as sRequest } from '@spinajs/http';
|
|
4
|
+
import { Counter, Gauge, Histogram } from 'prom-client';
|
|
5
|
+
import { Metrics } from './metrics.js';
|
|
6
|
+
import { TelemetryStore } from './store.js';
|
|
7
|
+
/**
|
|
8
|
+
* Default metric-name prefix for the HTTP telemetry metrics.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEFAULT_PREFIX = "http";
|
|
11
|
+
/**
|
|
12
|
+
* Histogram bucket boundaries ( ms ) for request duration.
|
|
13
|
+
*/
|
|
14
|
+
export declare const DURATION_BUCKETS_MS: number[];
|
|
15
|
+
/**
|
|
16
|
+
* HTTP request-timing / metrics middleware.
|
|
17
|
+
*
|
|
18
|
+
* Times every request and records:
|
|
19
|
+
* - `${prefix}_requests_total{method,route,status}` — a Counter,
|
|
20
|
+
* - `${prefix}_request_duration_ms{method,route,status}` — a Histogram,
|
|
21
|
+
* - `${prefix}_requests_in_flight` — a Gauge,
|
|
22
|
+
* against the DI-resolved {@link Metrics} registry, and feeds the shared
|
|
23
|
+
* {@link TelemetryStore} that backs the JSON stats endpoints.
|
|
24
|
+
*
|
|
25
|
+
* All of the end-of-request work runs from a `res.on( 'finish' )` handler
|
|
26
|
+
* registered in `before()` — `ServerMiddleware.after()` is never reached for a
|
|
27
|
+
* normal matched route, the same reason http's own `AccessLog` / `PerfRollup`
|
|
28
|
+
* middlewares work this way. A `res.on( 'close' )` handler balances the in-flight
|
|
29
|
+
* gauge — and only the gauge — for requests the client aborted, where `'finish'`
|
|
30
|
+
* never fires. Every bit of it is guarded so telemetry can never break a request.
|
|
31
|
+
*/
|
|
32
|
+
export declare class TelemetryMiddleware extends ServerMiddleware {
|
|
33
|
+
protected Log: Log;
|
|
34
|
+
/**
|
|
35
|
+
* The shared aggregates. This middleware is the only writer.
|
|
36
|
+
*/
|
|
37
|
+
protected Store: TelemetryStore;
|
|
38
|
+
/**
|
|
39
|
+
* Metric-name prefix. Set it with the `telemetry.prefix` configuration key —
|
|
40
|
+
* that is the supported way to re-prefix, and it needs no subclass.
|
|
41
|
+
*
|
|
42
|
+
* The precedence implemented in {@link ensureMetrics} is: a value assigned to
|
|
43
|
+
* this field that differs from {@link DEFAULT_PREFIX} wins, otherwise
|
|
44
|
+
* `telemetry.prefix` is used, otherwise {@link DEFAULT_PREFIX}. That override
|
|
45
|
+
* exists for subclasses that are replacing the middleware wholesale — see the
|
|
46
|
+
* README's "Replacing the middleware" section, which also covers the
|
|
47
|
+
* double-registration hazard of subclassing. Do NOT subclass merely to change
|
|
48
|
+
* the prefix.
|
|
49
|
+
*/
|
|
50
|
+
protected Prefix: string;
|
|
51
|
+
protected ConfiguredPrefix: string;
|
|
52
|
+
protected Buckets: number[];
|
|
53
|
+
protected metrics: Metrics;
|
|
54
|
+
protected requestsTotal: Counter<string>;
|
|
55
|
+
protected requestDuration: Histogram<string>;
|
|
56
|
+
protected inFlight: Gauge<string>;
|
|
57
|
+
private defined;
|
|
58
|
+
constructor();
|
|
59
|
+
/**
|
|
60
|
+
* Lazily resolve {@link Metrics} and define the metric set on first use.
|
|
61
|
+
* Idempotent — {@link Metrics.defineMetrics} removes+recreates duplicates.
|
|
62
|
+
*/
|
|
63
|
+
protected ensureMetrics(): void;
|
|
64
|
+
/**
|
|
65
|
+
* Derive a stable, low-cardinality route label. Prefers the matched route
|
|
66
|
+
* path ( `req.route.path` or `req.storage.route` ) over the raw request path.
|
|
67
|
+
*/
|
|
68
|
+
protected routeLabel(req: sRequest): string;
|
|
69
|
+
/**
|
|
70
|
+
* Do the end-of-request accounting. Called from the response `finish` event,
|
|
71
|
+
* never from `after()`.
|
|
72
|
+
*/
|
|
73
|
+
protected onFinish(req: sRequest, res: express.Response): void;
|
|
74
|
+
before(): (req: sRequest, res: express.Response, next: express.NextFunction) => void;
|
|
75
|
+
/**
|
|
76
|
+
* Nothing to do after the controllers — express does not run this for a
|
|
77
|
+
* matched route. See {@link onFinish}.
|
|
78
|
+
*/
|
|
79
|
+
after(): null;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAGnC,OAAO,EAAU,GAAG,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEtE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,OAAO,EAAa,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C;;GAEG;AACH,eAAO,MAAM,cAAc,SAAS,CAAC;AAErC;;GAEG;AACH,eAAO,MAAM,mBAAmB,UAA0D,CAAC;AAO3F;;;;;;;;;;;;;;;;GAgBG;AACH,qBACa,mBAAoB,SAAQ,gBAAgB;IAEvD,SAAS,CAAC,GAAG,EAAG,GAAG,CAAC;IAEpB;;OAEG;IAEH,SAAS,CAAC,KAAK,EAAG,cAAc,CAAC;IAEjC;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,MAAM,SAAkB;IAGlC,SAAS,CAAC,gBAAgB,EAAG,MAAM,CAAC;IAGpC,SAAS,CAAC,OAAO,EAAG,MAAM,EAAE,CAAC;IAE7B,SAAS,CAAC,OAAO,EAAG,OAAO,CAAC;IAC5B,SAAS,CAAC,aAAa,EAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,SAAS,CAAC,eAAe,EAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAC9C,SAAS,CAAC,QAAQ,EAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAEnC,OAAO,CAAC,OAAO,CAAS;;IASxB;;;OAGG;IACH,SAAS,CAAC,aAAa,IAAI,IAAI;IAmC/B;;;OAGG;IACH,SAAS,CAAC,UAAU,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM;IAO3C;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,CAAC,QAAQ,GAAG,IAAI;IA0BvD,MAAM,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,YAAY,KAAK,IAAI;IA0C3F;;;OAGG;IACI,KAAK,IAAI,IAAI;CAGrB"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { Autoinject, DI, Injectable } from '@spinajs/di';
|
|
11
|
+
import { Config } from '@spinajs/configuration';
|
|
12
|
+
import { Logger, Log } from '@spinajs/log';
|
|
13
|
+
import { ServerMiddleware } from '@spinajs/http';
|
|
14
|
+
import { Metrics } from './metrics.js';
|
|
15
|
+
import { TelemetryStore } from './store.js';
|
|
16
|
+
/**
|
|
17
|
+
* Default metric-name prefix for the HTTP telemetry metrics.
|
|
18
|
+
*/
|
|
19
|
+
export const DEFAULT_PREFIX = 'http';
|
|
20
|
+
/**
|
|
21
|
+
* Histogram bucket boundaries ( ms ) for request duration.
|
|
22
|
+
*/
|
|
23
|
+
export const DURATION_BUCKETS_MS = [5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000];
|
|
24
|
+
/**
|
|
25
|
+
* Storage key under `req.storage` holding the per-request start time.
|
|
26
|
+
*/
|
|
27
|
+
const START_KEY = '__telemetryStart';
|
|
28
|
+
/**
|
|
29
|
+
* HTTP request-timing / metrics middleware.
|
|
30
|
+
*
|
|
31
|
+
* Times every request and records:
|
|
32
|
+
* - `${prefix}_requests_total{method,route,status}` — a Counter,
|
|
33
|
+
* - `${prefix}_request_duration_ms{method,route,status}` — a Histogram,
|
|
34
|
+
* - `${prefix}_requests_in_flight` — a Gauge,
|
|
35
|
+
* against the DI-resolved {@link Metrics} registry, and feeds the shared
|
|
36
|
+
* {@link TelemetryStore} that backs the JSON stats endpoints.
|
|
37
|
+
*
|
|
38
|
+
* All of the end-of-request work runs from a `res.on( 'finish' )` handler
|
|
39
|
+
* registered in `before()` — `ServerMiddleware.after()` is never reached for a
|
|
40
|
+
* normal matched route, the same reason http's own `AccessLog` / `PerfRollup`
|
|
41
|
+
* middlewares work this way. A `res.on( 'close' )` handler balances the in-flight
|
|
42
|
+
* gauge — and only the gauge — for requests the client aborted, where `'finish'`
|
|
43
|
+
* never fires. Every bit of it is guarded so telemetry can never break a request.
|
|
44
|
+
*/
|
|
45
|
+
let TelemetryMiddleware = class TelemetryMiddleware extends ServerMiddleware {
|
|
46
|
+
constructor() {
|
|
47
|
+
super();
|
|
48
|
+
/**
|
|
49
|
+
* Metric-name prefix. Set it with the `telemetry.prefix` configuration key —
|
|
50
|
+
* that is the supported way to re-prefix, and it needs no subclass.
|
|
51
|
+
*
|
|
52
|
+
* The precedence implemented in {@link ensureMetrics} is: a value assigned to
|
|
53
|
+
* this field that differs from {@link DEFAULT_PREFIX} wins, otherwise
|
|
54
|
+
* `telemetry.prefix` is used, otherwise {@link DEFAULT_PREFIX}. That override
|
|
55
|
+
* exists for subclasses that are replacing the middleware wholesale — see the
|
|
56
|
+
* README's "Replacing the middleware" section, which also covers the
|
|
57
|
+
* double-registration hazard of subclassing. Do NOT subclass merely to change
|
|
58
|
+
* the prefix.
|
|
59
|
+
*/
|
|
60
|
+
this.Prefix = DEFAULT_PREFIX;
|
|
61
|
+
this.defined = false;
|
|
62
|
+
// Needs req.storage ( ReqStorage = -2 ) and sits alongside RequestId /
|
|
63
|
+
// AccessLog ( Order 2 ).
|
|
64
|
+
this.Order = 2;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Lazily resolve {@link Metrics} and define the metric set on first use.
|
|
68
|
+
* Idempotent — {@link Metrics.defineMetrics} removes+recreates duplicates.
|
|
69
|
+
*/
|
|
70
|
+
ensureMetrics() {
|
|
71
|
+
if (this.defined)
|
|
72
|
+
return;
|
|
73
|
+
this.metrics = DI.get(Metrics) ?? DI.resolve(Metrics);
|
|
74
|
+
// A subclass that overrode Prefix keeps it; otherwise configuration wins.
|
|
75
|
+
const prefix = this.Prefix !== DEFAULT_PREFIX ? this.Prefix : this.ConfiguredPrefix ?? DEFAULT_PREFIX;
|
|
76
|
+
const map = this.metrics.defineMetrics(prefix, [
|
|
77
|
+
{
|
|
78
|
+
name: 'requests_total',
|
|
79
|
+
help: 'Total number of HTTP requests',
|
|
80
|
+
type: 'counter',
|
|
81
|
+
labelNames: ['method', 'route', 'status'],
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'request_duration_ms',
|
|
85
|
+
help: 'HTTP request duration in milliseconds',
|
|
86
|
+
type: 'histogram',
|
|
87
|
+
labelNames: ['method', 'route', 'status'],
|
|
88
|
+
buckets: this.Buckets ?? DURATION_BUCKETS_MS,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: 'requests_in_flight',
|
|
92
|
+
help: 'Number of HTTP requests currently in flight',
|
|
93
|
+
type: 'gauge',
|
|
94
|
+
},
|
|
95
|
+
]);
|
|
96
|
+
this.requestsTotal = map['requests_total'];
|
|
97
|
+
this.requestDuration = map['request_duration_ms'];
|
|
98
|
+
this.inFlight = map['requests_in_flight'];
|
|
99
|
+
this.defined = true;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Derive a stable, low-cardinality route label. Prefers the matched route
|
|
103
|
+
* path ( `req.route.path` or `req.storage.route` ) over the raw request path.
|
|
104
|
+
*/
|
|
105
|
+
routeLabel(req) {
|
|
106
|
+
const anyReq = req;
|
|
107
|
+
const matched = anyReq.route?.path ?? req.storage?.route;
|
|
108
|
+
if (typeof matched === 'string' && matched.length > 0)
|
|
109
|
+
return matched;
|
|
110
|
+
return req.path ?? anyReq.originalUrl ?? 'unknown';
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Do the end-of-request accounting. Called from the response `finish` event,
|
|
114
|
+
* never from `after()`.
|
|
115
|
+
*/
|
|
116
|
+
onFinish(req, res) {
|
|
117
|
+
try {
|
|
118
|
+
const start = req.storage?.[START_KEY];
|
|
119
|
+
const durationMs = start !== undefined ? Number(process.hrtime.bigint() - start) / 1e6 : 0;
|
|
120
|
+
const method = req.method ?? 'GET';
|
|
121
|
+
// Read the route label HERE, not in before(): `req.route` is only
|
|
122
|
+
// populated once the router has matched.
|
|
123
|
+
const route = this.routeLabel(req);
|
|
124
|
+
const status = res.statusCode;
|
|
125
|
+
const labels = { method, route, status: String(status) };
|
|
126
|
+
// Balance before()'s inc() FIRST: a throw from anything below would
|
|
127
|
+
// otherwise strand the gauge one higher for the rest of the process.
|
|
128
|
+
this.inFlight.dec();
|
|
129
|
+
this.requestDuration.observe(labels, durationMs);
|
|
130
|
+
this.requestsTotal.inc(labels);
|
|
131
|
+
this.Store.record(method, route, status, durationMs);
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
this.Log?.warn(err, 'telemetry finish handler failed');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
before() {
|
|
138
|
+
return (req, res, next) => {
|
|
139
|
+
try {
|
|
140
|
+
this.ensureMetrics();
|
|
141
|
+
req.storage[START_KEY] = process.hrtime.bigint();
|
|
142
|
+
this.inFlight.inc();
|
|
143
|
+
// The inc() above must be balanced exactly once, whichever event wins.
|
|
144
|
+
// 'finish' does NOT fire when the client disconnects mid-response, only
|
|
145
|
+
// 'close' does — without the 'close' arm the gauge drifts monotonically
|
|
146
|
+
// upward for the life of the process behind a load balancer with client
|
|
147
|
+
// timeouts. On a normal request both fire, 'finish' first, so the flag
|
|
148
|
+
// is what keeps the gauge from being decremented twice.
|
|
149
|
+
let settled = false;
|
|
150
|
+
res.on('finish', () => {
|
|
151
|
+
if (settled)
|
|
152
|
+
return;
|
|
153
|
+
settled = true;
|
|
154
|
+
this.onFinish(req, res);
|
|
155
|
+
});
|
|
156
|
+
res.on('close', () => {
|
|
157
|
+
if (settled)
|
|
158
|
+
return;
|
|
159
|
+
settled = true;
|
|
160
|
+
// Aborted request: balance the gauge and NOTHING else. `res.statusCode`
|
|
161
|
+
// is still the un-sent default here, so recording it would pollute the
|
|
162
|
+
// status-class counters and the route stats with a response that was
|
|
163
|
+
// never sent.
|
|
164
|
+
try {
|
|
165
|
+
this.inFlight.dec();
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
this.Log?.warn(err, 'telemetry close handler failed');
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
this.Log?.warn(err, 'telemetry before() failed');
|
|
174
|
+
}
|
|
175
|
+
next();
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Nothing to do after the controllers — express does not run this for a
|
|
180
|
+
* matched route. See {@link onFinish}.
|
|
181
|
+
*/
|
|
182
|
+
after() {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
__decorate([
|
|
187
|
+
Logger('telemetry'),
|
|
188
|
+
__metadata("design:type", Log)
|
|
189
|
+
], TelemetryMiddleware.prototype, "Log", void 0);
|
|
190
|
+
__decorate([
|
|
191
|
+
Autoinject(TelemetryStore),
|
|
192
|
+
__metadata("design:type", TelemetryStore)
|
|
193
|
+
], TelemetryMiddleware.prototype, "Store", void 0);
|
|
194
|
+
__decorate([
|
|
195
|
+
Config('telemetry.prefix', { defaultValue: DEFAULT_PREFIX }),
|
|
196
|
+
__metadata("design:type", String)
|
|
197
|
+
], TelemetryMiddleware.prototype, "ConfiguredPrefix", void 0);
|
|
198
|
+
__decorate([
|
|
199
|
+
Config('telemetry.buckets', { defaultValue: DURATION_BUCKETS_MS }),
|
|
200
|
+
__metadata("design:type", Array)
|
|
201
|
+
], TelemetryMiddleware.prototype, "Buckets", void 0);
|
|
202
|
+
TelemetryMiddleware = __decorate([
|
|
203
|
+
Injectable(ServerMiddleware),
|
|
204
|
+
__metadata("design:paramtypes", [])
|
|
205
|
+
], TelemetryMiddleware);
|
|
206
|
+
export { TelemetryMiddleware };
|
|
207
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { BasePolicy, IController, IRoute, Request } from '@spinajs/http';
|
|
2
|
+
/**
|
|
3
|
+
* A policy that permits everything. Used as the explicit default for the
|
|
4
|
+
* liveness / readiness endpoints, which must be reachable by kubelet probes and
|
|
5
|
+
* load balancers that cannot carry a token.
|
|
6
|
+
*
|
|
7
|
+
* This exists so the config can name a real class. Leaving the policy config
|
|
8
|
+
* key empty would work, but the http layer logs `No policy named ... is
|
|
9
|
+
* registered` on every boot for an unresolvable value, which reads like a
|
|
10
|
+
* misconfiguration.
|
|
11
|
+
*/
|
|
12
|
+
export declare class PublicPolicy extends BasePolicy {
|
|
13
|
+
isEnabled(_action: IRoute, _instance: IController): boolean;
|
|
14
|
+
execute(_req: Request): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=PublicPolicy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PublicPolicy.d.ts","sourceRoot":"","sources":["../../../src/policies/PublicPolicy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAEzE;;;;;;;;;GASG;AACH,qBACa,YAAa,SAAQ,UAAU;IACnC,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,GAAG,OAAO;IAI3D,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAG7C"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { Injectable } from '@spinajs/di';
|
|
8
|
+
import { BasePolicy } from '@spinajs/http';
|
|
9
|
+
/**
|
|
10
|
+
* A policy that permits everything. Used as the explicit default for the
|
|
11
|
+
* liveness / readiness endpoints, which must be reachable by kubelet probes and
|
|
12
|
+
* load balancers that cannot carry a token.
|
|
13
|
+
*
|
|
14
|
+
* This exists so the config can name a real class. Leaving the policy config
|
|
15
|
+
* key empty would work, but the http layer logs `No policy named ... is
|
|
16
|
+
* registered` on every boot for an unresolvable value, which reads like a
|
|
17
|
+
* misconfiguration.
|
|
18
|
+
*/
|
|
19
|
+
let PublicPolicy = class PublicPolicy extends BasePolicy {
|
|
20
|
+
isEnabled(_action, _instance) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
execute(_req) {
|
|
24
|
+
return Promise.resolve();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
PublicPolicy = __decorate([
|
|
28
|
+
Injectable(BasePolicy)
|
|
29
|
+
], PublicPolicy);
|
|
30
|
+
export { PublicPolicy };
|
|
31
|
+
//# sourceMappingURL=PublicPolicy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PublicPolicy.js","sourceRoot":"","sources":["../../../src/policies/PublicPolicy.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAgC,MAAM,eAAe,CAAC;AAEzE;;;;;;;;;GASG;AAEI,IAAM,YAAY,GAAlB,MAAM,YAAa,SAAQ,UAAU;IACnC,SAAS,CAAC,OAAe,EAAE,SAAsB;QACtD,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,OAAO,CAAC,IAAa;QAC1B,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;CACF,CAAA;AARY,YAAY;IADxB,UAAU,CAAC,UAAU,CAAC;GACV,YAAY,CAQxB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { BasePolicy, IController, IRoute, Request } from '@spinajs/http';
|
|
2
|
+
import { Log } from '@spinajs/log';
|
|
3
|
+
/**
|
|
4
|
+
* Shared-token guard for the telemetry endpoints. Reads the expected token from
|
|
5
|
+
* `telemetry.auth.token` and compares it against the `x-metrics-token` header.
|
|
6
|
+
* The header name is deliberately unchanged from the retired
|
|
7
|
+
* `@spinajs/metrics` package so existing scrape configs keep working.
|
|
8
|
+
*
|
|
9
|
+
* Bypassed entirely in development.
|
|
10
|
+
*/
|
|
11
|
+
export declare class TelemetryTokenPolicy extends BasePolicy {
|
|
12
|
+
protected Log: Log;
|
|
13
|
+
protected Token: string;
|
|
14
|
+
protected isDev: boolean;
|
|
15
|
+
protected HEADER_TOKEN_FIELD: string;
|
|
16
|
+
isEnabled(_action: IRoute, _instance: IController): boolean;
|
|
17
|
+
execute(req: Request): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=TelemetryTokenPolicy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TelemetryTokenPolicy.d.ts","sourceRoot":"","sources":["../../../src/policies/TelemetryTokenPolicy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAEzE,OAAO,EAAE,GAAG,EAAU,MAAM,cAAc,CAAC;AAG3C;;;;;;;GAOG;AACH,qBACa,oBAAqB,SAAQ,UAAU;IAElD,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAGnB,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC;IAGxB,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC;IAEzB,SAAS,CAAC,kBAAkB,SAAqB;IAE1C,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,GAAG,OAAO;IAIrD,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAgBlD"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { Config } from '@spinajs/configuration';
|
|
11
|
+
import { BasePolicy } from '@spinajs/http';
|
|
12
|
+
import { Forbidden } from '@spinajs/exceptions';
|
|
13
|
+
import { Log, Logger } from '@spinajs/log';
|
|
14
|
+
import { Injectable } from '@spinajs/di';
|
|
15
|
+
/**
|
|
16
|
+
* Shared-token guard for the telemetry endpoints. Reads the expected token from
|
|
17
|
+
* `telemetry.auth.token` and compares it against the `x-metrics-token` header.
|
|
18
|
+
* The header name is deliberately unchanged from the retired
|
|
19
|
+
* `@spinajs/metrics` package so existing scrape configs keep working.
|
|
20
|
+
*
|
|
21
|
+
* Bypassed entirely in development.
|
|
22
|
+
*/
|
|
23
|
+
let TelemetryTokenPolicy = class TelemetryTokenPolicy extends BasePolicy {
|
|
24
|
+
constructor() {
|
|
25
|
+
super(...arguments);
|
|
26
|
+
this.HEADER_TOKEN_FIELD = 'x-metrics-token';
|
|
27
|
+
}
|
|
28
|
+
isEnabled(_action, _instance) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
async execute(req) {
|
|
32
|
+
if (this.isDev) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const token = req.headers[this.HEADER_TOKEN_FIELD];
|
|
36
|
+
if (!token) {
|
|
37
|
+
this.Log.warn(`No token is set for restricted area, header field: ${this.HEADER_TOKEN_FIELD}, policy: TelemetryTokenPolicy, ip: ${req.storage.realIp}`);
|
|
38
|
+
throw new Forbidden('access token is not set');
|
|
39
|
+
}
|
|
40
|
+
if (token !== this.Token) {
|
|
41
|
+
this.Log.warn(`Invalid access token received, header field: ${this.HEADER_TOKEN_FIELD}, policy: TelemetryTokenPolicy, ip: ${req.storage.realIp}`);
|
|
42
|
+
throw new Forbidden('invalid access token');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
__decorate([
|
|
47
|
+
Logger('Security'),
|
|
48
|
+
__metadata("design:type", Log)
|
|
49
|
+
], TelemetryTokenPolicy.prototype, "Log", void 0);
|
|
50
|
+
__decorate([
|
|
51
|
+
Config('telemetry.auth.token'),
|
|
52
|
+
__metadata("design:type", String)
|
|
53
|
+
], TelemetryTokenPolicy.prototype, "Token", void 0);
|
|
54
|
+
__decorate([
|
|
55
|
+
Config('configuration.isDevelopment'),
|
|
56
|
+
__metadata("design:type", Boolean)
|
|
57
|
+
], TelemetryTokenPolicy.prototype, "isDev", void 0);
|
|
58
|
+
TelemetryTokenPolicy = __decorate([
|
|
59
|
+
Injectable(BasePolicy)
|
|
60
|
+
], TelemetryTokenPolicy);
|
|
61
|
+
export { TelemetryTokenPolicy };
|
|
62
|
+
//# sourceMappingURL=TelemetryTokenPolicy.js.map
|