@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,60 @@
|
|
|
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 { BasePath, BaseController, Get, Policy } from '@spinajs/http';
|
|
11
|
+
import { Autoinject } from '@spinajs/di';
|
|
12
|
+
import { Metrics } from './../metrics.js';
|
|
13
|
+
import { PrometheusResponse } from './../responses.js';
|
|
14
|
+
// Importing the policy modules runs their @Injectable decorators, which is what
|
|
15
|
+
// puts the classes in the DI container under the names the routes below name as
|
|
16
|
+
// STRINGS ( via `telemetry.auth.policies.*` ). Nothing else forces them to load:
|
|
17
|
+
// this controller is mounted by the config-declared directory scan, and that
|
|
18
|
+
// configuration is auto-discovered from node_modules — so an app never has to
|
|
19
|
+
// import the package barrel. And http FAILS OPEN on an unresolvable policy name
|
|
20
|
+
// ( it logs `No policy named ...`, drops it, and a route left with no policies
|
|
21
|
+
// calls next() ), which would serve /metrics unauthenticated.
|
|
22
|
+
import './../policies/TelemetryTokenPolicy.js';
|
|
23
|
+
import './../policies/PublicPolicy.js';
|
|
24
|
+
/**
|
|
25
|
+
* Prometheus scrape endpoint.
|
|
26
|
+
*
|
|
27
|
+
* Deliberately kept at the bare `/metrics` path rather than under the
|
|
28
|
+
* `/telemetry` prefix used by the JSON api — this is the path existing
|
|
29
|
+
* `scrape_configs` already point at.
|
|
30
|
+
*/
|
|
31
|
+
let MetricsController = class MetricsController extends BaseController {
|
|
32
|
+
/**
|
|
33
|
+
* Prometheus exposition text for every metric on the registry: the `http_*`
|
|
34
|
+
* request metrics, the `perf_*` bridge metrics, any application metrics
|
|
35
|
+
* declared through `Metrics.defineMetrics`, and — when
|
|
36
|
+
* `telemetry.collectDefaultMetrics` is on — the `process_*` / `nodejs_*` set.
|
|
37
|
+
*
|
|
38
|
+
* @response 200 {string} Prometheus exposition text ( text/plain )
|
|
39
|
+
* @response 403 Access token missing or invalid
|
|
40
|
+
*/
|
|
41
|
+
async getMetrics() {
|
|
42
|
+
return new PrometheusResponse(this.MetricsService);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
__decorate([
|
|
46
|
+
Autoinject(Metrics),
|
|
47
|
+
__metadata("design:type", Metrics)
|
|
48
|
+
], MetricsController.prototype, "MetricsService", void 0);
|
|
49
|
+
__decorate([
|
|
50
|
+
Get('/'),
|
|
51
|
+
Policy('telemetry.auth.policies.metrics'),
|
|
52
|
+
__metadata("design:type", Function),
|
|
53
|
+
__metadata("design:paramtypes", []),
|
|
54
|
+
__metadata("design:returntype", Promise)
|
|
55
|
+
], MetricsController.prototype, "getMetrics", null);
|
|
56
|
+
MetricsController = __decorate([
|
|
57
|
+
BasePath('metrics')
|
|
58
|
+
], MetricsController);
|
|
59
|
+
export { MetricsController };
|
|
60
|
+
//# sourceMappingURL=Metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Metrics.js","sourceRoot":"","sources":["../../../src/controllers/Metrics.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,gFAAgF;AAChF,gFAAgF;AAChF,iFAAiF;AACjF,6EAA6E;AAC7E,8EAA8E;AAC9E,gFAAgF;AAChF,+EAA+E;AAC/E,8DAA8D;AAC9D,OAAO,uCAAuC,CAAC;AAC/C,OAAO,+BAA+B,CAAC;AAEvC;;;;;;GAMG;AAEI,IAAM,iBAAiB,GAAvB,MAAM,iBAAkB,SAAQ,cAAc;IAInD;;;;;;;;OAQG;IAGU,AAAN,KAAK,CAAC,UAAU;QACrB,OAAO,IAAI,kBAAkB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACrD,CAAC;CACF,CAAA;AAhBW;IADT,UAAU,CAAC,OAAO,CAAC;8BACM,OAAO;yDAAC;AAarB;IAFZ,GAAG,CAAC,GAAG,CAAC;IACR,MAAM,CAAC,iCAAiC,CAAC;;;;mDAGzC;AAjBU,iBAAiB;IAD7B,QAAQ,CAAC,SAAS,CAAC;GACP,iBAAiB,CAkB7B"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { BaseController, Ok } from '@spinajs/http';
|
|
2
|
+
import { TelemetryStore } from './../store.js';
|
|
3
|
+
import { InMemoryPerfSink } from './../InMemoryPerfSink.js';
|
|
4
|
+
import { HealthCheckRunner } from './../health.js';
|
|
5
|
+
import { ServiceUnavailable } from './../responses.js';
|
|
6
|
+
import './../dto/index.js';
|
|
7
|
+
import './../policies/TelemetryTokenPolicy.js';
|
|
8
|
+
import './../policies/PublicPolicy.js';
|
|
9
|
+
/**
|
|
10
|
+
* JSON telemetry API. Machine-facing — consumed by dashboards, uptime monitors
|
|
11
|
+
* and orchestrator probes.
|
|
12
|
+
*
|
|
13
|
+
* The prometheus scrape endpoint deliberately lives on its own controller at
|
|
14
|
+
* the bare `/metrics` path, not under this prefix.
|
|
15
|
+
*/
|
|
16
|
+
export declare class TelemetryController extends BaseController {
|
|
17
|
+
protected Store: TelemetryStore;
|
|
18
|
+
protected PerfSink: InMemoryPerfSink;
|
|
19
|
+
protected Health: HealthCheckRunner;
|
|
20
|
+
protected Version: string;
|
|
21
|
+
/**
|
|
22
|
+
* Lifetime request statistics plus the rolling timeline.
|
|
23
|
+
*
|
|
24
|
+
* @returns {StatsResponse} Lifetime stats and the per-bucket timeline
|
|
25
|
+
* @response 403 Access token missing or invalid
|
|
26
|
+
*/
|
|
27
|
+
getStats(): Promise<Ok<{
|
|
28
|
+
all: import("../requestStats.js").IRequestStatsSnapshot;
|
|
29
|
+
timeline: import("../timeline.js").ITimelineSnapshot;
|
|
30
|
+
}>>;
|
|
31
|
+
/**
|
|
32
|
+
* The rolling timeline alone, with each bucket annotated with the wall-clock
|
|
33
|
+
* window it covers so consumers do not have to reverse the bucket key.
|
|
34
|
+
*
|
|
35
|
+
* @param buckets - how many of the most recent buckets to return; all of them when omitted
|
|
36
|
+
* @returns {TimelineResponse} The annotated timeline
|
|
37
|
+
* @response 400 `buckets` was not a positive integer
|
|
38
|
+
* @response 403 Access token missing or invalid
|
|
39
|
+
*/
|
|
40
|
+
getTimeline(buckets?: string): Promise<Ok<{
|
|
41
|
+
bucketMs: number;
|
|
42
|
+
length: number;
|
|
43
|
+
buckets: {
|
|
44
|
+
key: number;
|
|
45
|
+
from: number;
|
|
46
|
+
to: number;
|
|
47
|
+
stats: import("../requestStats.js").IRequestStatsSnapshot;
|
|
48
|
+
}[];
|
|
49
|
+
}>>;
|
|
50
|
+
/**
|
|
51
|
+
* Per method+route request breakdown.
|
|
52
|
+
*
|
|
53
|
+
* No percentiles — query them from prometheus against the `route`-labelled
|
|
54
|
+
* `http_request_duration_ms` histogram, which carries the same label values.
|
|
55
|
+
*
|
|
56
|
+
* @returns {RoutesResponse} Per-route request statistics
|
|
57
|
+
* @response 403 Access token missing or invalid
|
|
58
|
+
*/
|
|
59
|
+
getRoutes(): Promise<Ok<import("../routeStats.js").IRouteStatsSnapshot>>;
|
|
60
|
+
/**
|
|
61
|
+
* Aggregated performance measurements collected through the `Perf` facade —
|
|
62
|
+
* every `Perf.measure`, `@Measure`, `Perf.count` and `Perf.value` in the
|
|
63
|
+
* process, including the ORM's `orm.query` spans.
|
|
64
|
+
*
|
|
65
|
+
* @returns {PerfResponse} Aggregated spans and events
|
|
66
|
+
* @response 403 Access token missing or invalid
|
|
67
|
+
*/
|
|
68
|
+
getPerf(): Promise<Ok<import("./../InMemoryPerfSink.js").IPerfSnapshot>>;
|
|
69
|
+
/**
|
|
70
|
+
* Liveness. Runs no checks, so it can never be made slow or flaky by a
|
|
71
|
+
* dependency — if this answers, the process is alive.
|
|
72
|
+
*
|
|
73
|
+
* @returns {HealthResponse} Process liveness information
|
|
74
|
+
*/
|
|
75
|
+
getHealth(): Promise<Ok<{
|
|
76
|
+
version?: string | undefined;
|
|
77
|
+
status: string;
|
|
78
|
+
startedAt: number;
|
|
79
|
+
uptimeMs: number;
|
|
80
|
+
pid: number;
|
|
81
|
+
node: string;
|
|
82
|
+
}>>;
|
|
83
|
+
/**
|
|
84
|
+
* Readiness. Runs every registered `HealthCheck`, each raced against
|
|
85
|
+
* `telemetry.health.timeoutMs`.
|
|
86
|
+
*
|
|
87
|
+
* @returns {ReadyResponse} Overall readiness and the per-check results
|
|
88
|
+
* @response 503 At least one check is down ( or degraded, when `telemetry.health.failOnDegraded` is set )
|
|
89
|
+
*/
|
|
90
|
+
getReady(): Promise<ServiceUnavailable<import("./../health.js").IReadyReport> | Ok<import("./../health.js").IReadyReport>>;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=Telemetry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Telemetry.d.ts","sourceRoot":"","sources":["../../../src/controllers/Telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,cAAc,EAAsB,EAAE,EAAE,MAAM,eAAe,CAAC;AAKjF,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAIvD,OAAO,mBAAmB,CAAC;AAW3B,OAAO,uCAAuC,CAAC;AAC/C,OAAO,+BAA+B,CAAC;AAEvC;;;;;;GAMG;AACH,qBACa,mBAAoB,SAAQ,cAAc;IAErD,SAAS,CAAC,KAAK,EAAE,cAAc,CAAC;IAGhC,SAAS,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IAGrC,SAAS,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAGpC,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;IAE1B;;;;;OAKG;IAGU,QAAQ;;;;IAarB;;;;;;;;OAQG;IAGU,WAAW,CAAU,OAAO,CAAC,EAAE,MAAM;;;;;;;;;;IA0ClD;;;;;;;;OAQG;IAGU,SAAS;IAItB;;;;;;;OAOG;IAGU,OAAO;IAIpB;;;;;OAKG;IAGU,SAAS;;;;;;;;IAetB;;;;;;OAMG;IAGU,QAAQ;CAStB"}
|
|
@@ -0,0 +1,223 @@
|
|
|
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
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { BasePath, BaseController, Get, Policy, Query, Ok } from '@spinajs/http';
|
|
14
|
+
import { Autoinject } from '@spinajs/di';
|
|
15
|
+
import { Config } from '@spinajs/configuration';
|
|
16
|
+
import { BadRequest } from '@spinajs/exceptions';
|
|
17
|
+
import { TelemetryStore } from './../store.js';
|
|
18
|
+
import { InMemoryPerfSink } from './../InMemoryPerfSink.js';
|
|
19
|
+
import { HealthCheckRunner } from './../health.js';
|
|
20
|
+
import { ServiceUnavailable } from './../responses.js';
|
|
21
|
+
// Importing the DTO barrel runs the @Schema decorators, which is what makes the
|
|
22
|
+
// response schemas resolvable by name in the generated OpenAPI document.
|
|
23
|
+
import './../dto/index.js';
|
|
24
|
+
// Same reason, for security rather than documentation: importing the policy
|
|
25
|
+
// modules runs their @Injectable decorators, which is what puts the classes in
|
|
26
|
+
// the DI container under the names the routes below name as STRINGS ( via
|
|
27
|
+
// `telemetry.auth.policies.*` ). Nothing else forces them to load — this
|
|
28
|
+
// controller is mounted by the config-declared directory scan, and that
|
|
29
|
+
// configuration is auto-discovered from node_modules, so an app never has to
|
|
30
|
+
// import the package barrel. And http FAILS OPEN on an unresolvable policy name
|
|
31
|
+
// ( it logs `No policy named ...`, drops it, and a route left with no policies
|
|
32
|
+
// calls next() ), which would serve these endpoints unauthenticated.
|
|
33
|
+
import './../policies/TelemetryTokenPolicy.js';
|
|
34
|
+
import './../policies/PublicPolicy.js';
|
|
35
|
+
/**
|
|
36
|
+
* JSON telemetry API. Machine-facing — consumed by dashboards, uptime monitors
|
|
37
|
+
* and orchestrator probes.
|
|
38
|
+
*
|
|
39
|
+
* The prometheus scrape endpoint deliberately lives on its own controller at
|
|
40
|
+
* the bare `/metrics` path, not under this prefix.
|
|
41
|
+
*/
|
|
42
|
+
let TelemetryController = class TelemetryController extends BaseController {
|
|
43
|
+
/**
|
|
44
|
+
* Lifetime request statistics plus the rolling timeline.
|
|
45
|
+
*
|
|
46
|
+
* @returns {StatsResponse} Lifetime stats and the per-bucket timeline
|
|
47
|
+
* @response 403 Access token missing or invalid
|
|
48
|
+
*/
|
|
49
|
+
async getStats() {
|
|
50
|
+
const stats = this.Store.RequestStats;
|
|
51
|
+
// Rates are not maintained on the hot path — they are derived here, over the
|
|
52
|
+
// whole collection window, or `req_rate` / `err_rate` would always read 0.
|
|
53
|
+
stats.updateRates(Date.now() - this.Store.StartedAt);
|
|
54
|
+
return new Ok({
|
|
55
|
+
all: stats.toJSON(),
|
|
56
|
+
timeline: this.Store.Timeline.toJSON(),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The rolling timeline alone, with each bucket annotated with the wall-clock
|
|
61
|
+
* window it covers so consumers do not have to reverse the bucket key.
|
|
62
|
+
*
|
|
63
|
+
* @param buckets - how many of the most recent buckets to return; all of them when omitted
|
|
64
|
+
* @returns {TimelineResponse} The annotated timeline
|
|
65
|
+
* @response 400 `buckets` was not a positive integer
|
|
66
|
+
* @response 403 Access token missing or invalid
|
|
67
|
+
*/
|
|
68
|
+
async getTimeline(buckets) {
|
|
69
|
+
const timeline = this.Store.Timeline;
|
|
70
|
+
const snapshot = timeline.toJSON();
|
|
71
|
+
let keys = Object.keys(snapshot)
|
|
72
|
+
.map((k) => Number(k))
|
|
73
|
+
.sort((a, b) => a - b);
|
|
74
|
+
// Only a genuinely ABSENT `buckets` means "all of them". An explicitly empty
|
|
75
|
+
// `?buckets=` is a supplied value that is not a positive integer, so it is
|
|
76
|
+
// rejected like every other invalid one rather than silently meaning "all".
|
|
77
|
+
if (buckets !== undefined && buckets !== null) {
|
|
78
|
+
// Taken as a string and parsed here rather than declared `number` and left
|
|
79
|
+
// to the decorator's schema: a query value arrives as a string, and how the
|
|
80
|
+
// framework coerces a non-numeric one ( `Number( 'abc' )` -> NaN, rejected
|
|
81
|
+
// by the arg validator with a generic message ) is not something this
|
|
82
|
+
// endpoint's contract should depend on.
|
|
83
|
+
//
|
|
84
|
+
// `Number( '' )` is 0, not NaN, so the empty string falls through to the
|
|
85
|
+
// `count < 1` rejection below on its own.
|
|
86
|
+
const count = Number(buckets);
|
|
87
|
+
if (!Number.isInteger(count) || count < 1) {
|
|
88
|
+
throw new BadRequest('buckets must be a positive integer');
|
|
89
|
+
}
|
|
90
|
+
// The most recent N — the tail of an ascending key order.
|
|
91
|
+
keys = keys.slice(-count);
|
|
92
|
+
}
|
|
93
|
+
return new Ok({
|
|
94
|
+
bucketMs: timeline.bucketMs,
|
|
95
|
+
length: timeline.length,
|
|
96
|
+
buckets: keys.map((key) => ({
|
|
97
|
+
key,
|
|
98
|
+
from: key * timeline.bucketMs,
|
|
99
|
+
to: (key + 1) * timeline.bucketMs,
|
|
100
|
+
stats: snapshot[String(key)],
|
|
101
|
+
})),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Per method+route request breakdown.
|
|
106
|
+
*
|
|
107
|
+
* No percentiles — query them from prometheus against the `route`-labelled
|
|
108
|
+
* `http_request_duration_ms` histogram, which carries the same label values.
|
|
109
|
+
*
|
|
110
|
+
* @returns {RoutesResponse} Per-route request statistics
|
|
111
|
+
* @response 403 Access token missing or invalid
|
|
112
|
+
*/
|
|
113
|
+
async getRoutes() {
|
|
114
|
+
return new Ok(this.Store.RouteStats.toJSON());
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Aggregated performance measurements collected through the `Perf` facade —
|
|
118
|
+
* every `Perf.measure`, `@Measure`, `Perf.count` and `Perf.value` in the
|
|
119
|
+
* process, including the ORM's `orm.query` spans.
|
|
120
|
+
*
|
|
121
|
+
* @returns {PerfResponse} Aggregated spans and events
|
|
122
|
+
* @response 403 Access token missing or invalid
|
|
123
|
+
*/
|
|
124
|
+
async getPerf() {
|
|
125
|
+
return new Ok(this.PerfSink.toJSON());
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Liveness. Runs no checks, so it can never be made slow or flaky by a
|
|
129
|
+
* dependency — if this answers, the process is alive.
|
|
130
|
+
*
|
|
131
|
+
* @returns {HealthResponse} Process liveness information
|
|
132
|
+
*/
|
|
133
|
+
async getHealth() {
|
|
134
|
+
const uptimeMs = Math.round(process.uptime() * 1000);
|
|
135
|
+
return new Ok({
|
|
136
|
+
status: 'up',
|
|
137
|
+
startedAt: Date.now() - uptimeMs,
|
|
138
|
+
uptimeMs,
|
|
139
|
+
pid: process.pid,
|
|
140
|
+
node: process.version,
|
|
141
|
+
// omitted rather than reported as null when `telemetry.health.version` is unset
|
|
142
|
+
...(this.Version ? { version: this.Version } : {}),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Readiness. Runs every registered `HealthCheck`, each raced against
|
|
147
|
+
* `telemetry.health.timeoutMs`.
|
|
148
|
+
*
|
|
149
|
+
* @returns {ReadyResponse} Overall readiness and the per-check results
|
|
150
|
+
* @response 503 At least one check is down ( or degraded, when `telemetry.health.failOnDegraded` is set )
|
|
151
|
+
*/
|
|
152
|
+
async getReady() {
|
|
153
|
+
const report = await this.Health.run();
|
|
154
|
+
if (this.Health.isFailing(report.status)) {
|
|
155
|
+
return new ServiceUnavailable(report);
|
|
156
|
+
}
|
|
157
|
+
return new Ok(report);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
__decorate([
|
|
161
|
+
Autoinject(TelemetryStore),
|
|
162
|
+
__metadata("design:type", TelemetryStore)
|
|
163
|
+
], TelemetryController.prototype, "Store", void 0);
|
|
164
|
+
__decorate([
|
|
165
|
+
Autoinject(InMemoryPerfSink),
|
|
166
|
+
__metadata("design:type", InMemoryPerfSink)
|
|
167
|
+
], TelemetryController.prototype, "PerfSink", void 0);
|
|
168
|
+
__decorate([
|
|
169
|
+
Autoinject(HealthCheckRunner),
|
|
170
|
+
__metadata("design:type", HealthCheckRunner)
|
|
171
|
+
], TelemetryController.prototype, "Health", void 0);
|
|
172
|
+
__decorate([
|
|
173
|
+
Config('telemetry.health.version'),
|
|
174
|
+
__metadata("design:type", String)
|
|
175
|
+
], TelemetryController.prototype, "Version", void 0);
|
|
176
|
+
__decorate([
|
|
177
|
+
Get('stats'),
|
|
178
|
+
Policy('telemetry.auth.policies.stats'),
|
|
179
|
+
__metadata("design:type", Function),
|
|
180
|
+
__metadata("design:paramtypes", []),
|
|
181
|
+
__metadata("design:returntype", Promise)
|
|
182
|
+
], TelemetryController.prototype, "getStats", null);
|
|
183
|
+
__decorate([
|
|
184
|
+
Get('timeline'),
|
|
185
|
+
Policy('telemetry.auth.policies.timeline'),
|
|
186
|
+
__param(0, Query()),
|
|
187
|
+
__metadata("design:type", Function),
|
|
188
|
+
__metadata("design:paramtypes", [String]),
|
|
189
|
+
__metadata("design:returntype", Promise)
|
|
190
|
+
], TelemetryController.prototype, "getTimeline", null);
|
|
191
|
+
__decorate([
|
|
192
|
+
Get('routes'),
|
|
193
|
+
Policy('telemetry.auth.policies.routes'),
|
|
194
|
+
__metadata("design:type", Function),
|
|
195
|
+
__metadata("design:paramtypes", []),
|
|
196
|
+
__metadata("design:returntype", Promise)
|
|
197
|
+
], TelemetryController.prototype, "getRoutes", null);
|
|
198
|
+
__decorate([
|
|
199
|
+
Get('perf'),
|
|
200
|
+
Policy('telemetry.auth.policies.perf'),
|
|
201
|
+
__metadata("design:type", Function),
|
|
202
|
+
__metadata("design:paramtypes", []),
|
|
203
|
+
__metadata("design:returntype", Promise)
|
|
204
|
+
], TelemetryController.prototype, "getPerf", null);
|
|
205
|
+
__decorate([
|
|
206
|
+
Get('health'),
|
|
207
|
+
Policy('telemetry.auth.policies.health'),
|
|
208
|
+
__metadata("design:type", Function),
|
|
209
|
+
__metadata("design:paramtypes", []),
|
|
210
|
+
__metadata("design:returntype", Promise)
|
|
211
|
+
], TelemetryController.prototype, "getHealth", null);
|
|
212
|
+
__decorate([
|
|
213
|
+
Get('ready'),
|
|
214
|
+
Policy('telemetry.auth.policies.ready'),
|
|
215
|
+
__metadata("design:type", Function),
|
|
216
|
+
__metadata("design:paramtypes", []),
|
|
217
|
+
__metadata("design:returntype", Promise)
|
|
218
|
+
], TelemetryController.prototype, "getReady", null);
|
|
219
|
+
TelemetryController = __decorate([
|
|
220
|
+
BasePath('telemetry')
|
|
221
|
+
], TelemetryController);
|
|
222
|
+
export { TelemetryController };
|
|
223
|
+
//# sourceMappingURL=Telemetry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Telemetry.js","sourceRoot":"","sources":["../../../src/controllers/Telemetry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response shapes for the telemetry endpoints.
|
|
3
|
+
*
|
|
4
|
+
* These classes are never instantiated — they exist so `@Schema` registers each
|
|
5
|
+
* JSON schema under the class name, which is how http-swagger resolves a
|
|
6
|
+
* `@returns {StatsResponse}` JSDoc tag into a `#/components/schemas/...`
|
|
7
|
+
* component. The controller imports this module so the decorators run.
|
|
8
|
+
*/
|
|
9
|
+
export declare class StatsResponse {
|
|
10
|
+
}
|
|
11
|
+
export declare class TimelineResponse {
|
|
12
|
+
}
|
|
13
|
+
export declare class RoutesResponse {
|
|
14
|
+
}
|
|
15
|
+
export declare class PerfResponse {
|
|
16
|
+
}
|
|
17
|
+
export declare class HealthResponse {
|
|
18
|
+
}
|
|
19
|
+
export declare class ReadyResponse {
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dto/index.ts"],"names":[],"mappings":"AASA;;;;;;;GAOG;AAEH,qBACa,aAAa;CAAG;AAE7B,qBACa,gBAAgB;CAAG;AAEhC,qBACa,cAAc;CAAG;AAE9B,qBACa,YAAY;CAAG;AAE5B,qBACa,cAAc;CAAG;AAE9B,qBACa,aAAa;CAAG"}
|
|
@@ -0,0 +1,58 @@
|
|
|
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 { Schema } from '@spinajs/validation';
|
|
8
|
+
import StatsResponseSchema from './../schemas/StatsResponse.schema.js';
|
|
9
|
+
import TimelineResponseSchema from './../schemas/TimelineResponse.schema.js';
|
|
10
|
+
import RoutesResponseSchema from './../schemas/RoutesResponse.schema.js';
|
|
11
|
+
import PerfResponseSchema from './../schemas/PerfResponse.schema.js';
|
|
12
|
+
import HealthResponseSchema from './../schemas/HealthResponse.schema.js';
|
|
13
|
+
import ReadyResponseSchema from './../schemas/ReadyResponse.schema.js';
|
|
14
|
+
/**
|
|
15
|
+
* Response shapes for the telemetry endpoints.
|
|
16
|
+
*
|
|
17
|
+
* These classes are never instantiated — they exist so `@Schema` registers each
|
|
18
|
+
* JSON schema under the class name, which is how http-swagger resolves a
|
|
19
|
+
* `@returns {StatsResponse}` JSDoc tag into a `#/components/schemas/...`
|
|
20
|
+
* component. The controller imports this module so the decorators run.
|
|
21
|
+
*/
|
|
22
|
+
let StatsResponse = class StatsResponse {
|
|
23
|
+
};
|
|
24
|
+
StatsResponse = __decorate([
|
|
25
|
+
Schema(StatsResponseSchema)
|
|
26
|
+
], StatsResponse);
|
|
27
|
+
export { StatsResponse };
|
|
28
|
+
let TimelineResponse = class TimelineResponse {
|
|
29
|
+
};
|
|
30
|
+
TimelineResponse = __decorate([
|
|
31
|
+
Schema(TimelineResponseSchema)
|
|
32
|
+
], TimelineResponse);
|
|
33
|
+
export { TimelineResponse };
|
|
34
|
+
let RoutesResponse = class RoutesResponse {
|
|
35
|
+
};
|
|
36
|
+
RoutesResponse = __decorate([
|
|
37
|
+
Schema(RoutesResponseSchema)
|
|
38
|
+
], RoutesResponse);
|
|
39
|
+
export { RoutesResponse };
|
|
40
|
+
let PerfResponse = class PerfResponse {
|
|
41
|
+
};
|
|
42
|
+
PerfResponse = __decorate([
|
|
43
|
+
Schema(PerfResponseSchema)
|
|
44
|
+
], PerfResponse);
|
|
45
|
+
export { PerfResponse };
|
|
46
|
+
let HealthResponse = class HealthResponse {
|
|
47
|
+
};
|
|
48
|
+
HealthResponse = __decorate([
|
|
49
|
+
Schema(HealthResponseSchema)
|
|
50
|
+
], HealthResponse);
|
|
51
|
+
export { HealthResponse };
|
|
52
|
+
let ReadyResponse = class ReadyResponse {
|
|
53
|
+
};
|
|
54
|
+
ReadyResponse = __decorate([
|
|
55
|
+
Schema(ReadyResponseSchema)
|
|
56
|
+
], ReadyResponse);
|
|
57
|
+
export { ReadyResponse };
|
|
58
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/dto/index.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C,OAAO,mBAAmB,MAAM,sCAAsC,CAAC;AACvE,OAAO,sBAAsB,MAAM,yCAAyC,CAAC;AAC7E,OAAO,oBAAoB,MAAM,uCAAuC,CAAC;AACzE,OAAO,kBAAkB,MAAM,qCAAqC,CAAC;AACrE,OAAO,oBAAoB,MAAM,uCAAuC,CAAC;AACzE,OAAO,mBAAmB,MAAM,sCAAsC,CAAC;AAEvE;;;;;;;GAOG;AAGI,IAAM,aAAa,GAAnB,MAAM,aAAa;CAAG,CAAA;AAAhB,aAAa;IADzB,MAAM,CAAC,mBAAmB,CAAC;GACf,aAAa,CAAG;;AAGtB,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;CAAG,CAAA;AAAnB,gBAAgB;IAD5B,MAAM,CAAC,sBAAsB,CAAC;GAClB,gBAAgB,CAAG;;AAGzB,IAAM,cAAc,GAApB,MAAM,cAAc;CAAG,CAAA;AAAjB,cAAc;IAD1B,MAAM,CAAC,oBAAoB,CAAC;GAChB,cAAc,CAAG;;AAGvB,IAAM,YAAY,GAAlB,MAAM,YAAY;CAAG,CAAA;AAAf,YAAY;IADxB,MAAM,CAAC,kBAAkB,CAAC;GACd,YAAY,CAAG;;AAGrB,IAAM,cAAc,GAApB,MAAM,cAAc;CAAG,CAAA;AAAjB,cAAc;IAD1B,MAAM,CAAC,oBAAoB,CAAC;GAChB,cAAc,CAAG;;AAGvB,IAAM,aAAa,GAAnB,MAAM,aAAa;CAAG,CAAA;AAAhB,aAAa;IADzB,MAAM,CAAC,mBAAmB,CAAC;GACf,aAAa,CAAG"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Metrics } from './metrics.js';
|
|
2
|
+
import { TelemetryStore } from './store.js';
|
|
3
|
+
/**
|
|
4
|
+
* Minimal request/response shapes the endpoint handlers rely on. Kept
|
|
5
|
+
* intentionally structural ( `setHeader` / `end` ) so the handlers are not
|
|
6
|
+
* coupled to express beyond what a plain Node `ServerResponse` provides.
|
|
7
|
+
*/
|
|
8
|
+
export interface IMinimalResponse {
|
|
9
|
+
setHeader(name: string, value: string): void;
|
|
10
|
+
statusCode?: number;
|
|
11
|
+
end(body?: string): void;
|
|
12
|
+
}
|
|
13
|
+
export type MinimalRequest = unknown;
|
|
14
|
+
/**
|
|
15
|
+
* Build the Prometheus `/metrics` scrape handler.
|
|
16
|
+
*
|
|
17
|
+
* Sets `Content-Type` from the registry and writes the ( async-rendered )
|
|
18
|
+
* exposition text.
|
|
19
|
+
*/
|
|
20
|
+
export declare function metricsHandler(metrics: Metrics): (req: MinimalRequest, res: IMinimalResponse) => Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Build the JSON stats handler.
|
|
23
|
+
*
|
|
24
|
+
* Writes `{ all: RequestStats.toJSON(), timeline: Timeline.toJSON() }` read
|
|
25
|
+
* from the shared telemetry store — the same counters `GET /telemetry/stats`
|
|
26
|
+
* serves. The rate fields differ though: only the controller endpoint calls
|
|
27
|
+
* `RequestStats.updateRates( ... )`, and because that MUTATES the shared
|
|
28
|
+
* `RequestStats`, the `req_rate` / `err_rate` this handler emits are whatever
|
|
29
|
+
* the last `/telemetry/stats` call left behind ( `0` if nobody has called it ).
|
|
30
|
+
*/
|
|
31
|
+
export declare function statsHandler(store: TelemetryStore): (req: MinimalRequest, res: IMinimalResponse) => void;
|
|
32
|
+
//# sourceMappingURL=endpoints.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpoints.d.ts","sourceRoot":"","sources":["../../src/endpoints.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC;AAErC;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAM9G;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,GAAG,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,gBAAgB,KAAK,IAAI,CASxG"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the Prometheus `/metrics` scrape handler.
|
|
3
|
+
*
|
|
4
|
+
* Sets `Content-Type` from the registry and writes the ( async-rendered )
|
|
5
|
+
* exposition text.
|
|
6
|
+
*/
|
|
7
|
+
export function metricsHandler(metrics) {
|
|
8
|
+
return async (_req, res) => {
|
|
9
|
+
const body = await metrics.render();
|
|
10
|
+
res.setHeader('Content-Type', metrics.contentType());
|
|
11
|
+
res.end(body);
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Build the JSON stats handler.
|
|
16
|
+
*
|
|
17
|
+
* Writes `{ all: RequestStats.toJSON(), timeline: Timeline.toJSON() }` read
|
|
18
|
+
* from the shared telemetry store — the same counters `GET /telemetry/stats`
|
|
19
|
+
* serves. The rate fields differ though: only the controller endpoint calls
|
|
20
|
+
* `RequestStats.updateRates( ... )`, and because that MUTATES the shared
|
|
21
|
+
* `RequestStats`, the `req_rate` / `err_rate` this handler emits are whatever
|
|
22
|
+
* the last `/telemetry/stats` call left behind ( `0` if nobody has called it ).
|
|
23
|
+
*/
|
|
24
|
+
export function statsHandler(store) {
|
|
25
|
+
return (_req, res) => {
|
|
26
|
+
const body = JSON.stringify({
|
|
27
|
+
all: store.RequestStats.toJSON(),
|
|
28
|
+
timeline: store.Timeline.toJSON(),
|
|
29
|
+
});
|
|
30
|
+
res.setHeader('Content-Type', 'application/json');
|
|
31
|
+
res.end(body);
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=endpoints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpoints.js","sourceRoot":"","sources":["../../src/endpoints.ts"],"names":[],"mappings":"AAgBA;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAAgB;IAC7C,OAAO,KAAK,EAAE,IAAoB,EAAE,GAAqB,EAAiB,EAAE;QAC1E,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC;QACpC,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;QACrD,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,KAAqB;IAChD,OAAO,CAAC,IAAoB,EAAE,GAAqB,EAAQ,EAAE;QAC3D,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;YAC1B,GAAG,EAAE,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE;YAChC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE;SAClC,CAAC,CAAC;QACH,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;QAClD,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { AsyncService } from '@spinajs/di';
|
|
2
|
+
/** Outcome of a single health check. */
|
|
3
|
+
export type HealthStatus = 'up' | 'degraded' | 'down';
|
|
4
|
+
/** What a {@link HealthCheck} returns. */
|
|
5
|
+
export interface IHealthResult {
|
|
6
|
+
status: HealthStatus;
|
|
7
|
+
message?: string;
|
|
8
|
+
data?: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
/** One check's line in the readiness report. */
|
|
11
|
+
export interface IHealthCheckReport extends IHealthResult {
|
|
12
|
+
name: string;
|
|
13
|
+
/** How long the check took, in ms. */
|
|
14
|
+
durationMs: number;
|
|
15
|
+
}
|
|
16
|
+
/** The body served by `GET /telemetry/ready`. */
|
|
17
|
+
export interface IReadyReport {
|
|
18
|
+
status: HealthStatus;
|
|
19
|
+
checks: IHealthCheckReport[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* A readiness probe for one dependency.
|
|
23
|
+
*
|
|
24
|
+
* Register a concrete subclass with `@Injectable( HealthCheck )` and it is
|
|
25
|
+
* discovered by {@link HealthCheckRunner} via `Array.ofType( HealthCheck )`.
|
|
26
|
+
* `@spinajs/telemetry` deliberately ships NO concrete checks — a database check
|
|
27
|
+
* belongs where the database dependency already is, not in the observability
|
|
28
|
+
* package.
|
|
29
|
+
*
|
|
30
|
+
* `check()` may throw; a throw counts as `down`.
|
|
31
|
+
*/
|
|
32
|
+
export declare abstract class HealthCheck extends AsyncService {
|
|
33
|
+
/** Stable identifier for this check, used as the report key. */
|
|
34
|
+
abstract Name: string;
|
|
35
|
+
abstract check(): Promise<IHealthResult>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Runs every registered {@link HealthCheck} concurrently, each raced against a
|
|
39
|
+
* per-check timeout, and reduces the results to one overall status.
|
|
40
|
+
*
|
|
41
|
+
* The timeout is the point of the class: a readiness endpoint that can hang is
|
|
42
|
+
* worse than no readiness endpoint, because a kubelet probe blocks on it.
|
|
43
|
+
*/
|
|
44
|
+
export declare class HealthCheckRunner {
|
|
45
|
+
protected Checks: HealthCheck[];
|
|
46
|
+
protected TimeoutMs: number;
|
|
47
|
+
protected FailOnDegraded: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* True when the overall status should be served as HTTP 503.
|
|
50
|
+
*/
|
|
51
|
+
isFailing(status: HealthStatus): boolean;
|
|
52
|
+
run(): Promise<IReadyReport>;
|
|
53
|
+
private runOne;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/health.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAqC,MAAM,aAAa,CAAC;AAG9E,wCAAwC;AACxC,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,UAAU,GAAG,MAAM,CAAC;AAEtD,0CAA0C;AAC1C,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,gDAAgD;AAChD,MAAM,WAAW,kBAAmB,SAAQ,aAAa;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,iDAAiD;AACjD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,kBAAkB,EAAE,CAAC;CAC9B;AAED;;;;;;;;;;GAUG;AACH,8BAAsB,WAAY,SAAQ,YAAY;IACpD,gEAAgE;IAChE,SAAgB,IAAI,EAAE,MAAM,CAAC;aAEb,KAAK,IAAI,OAAO,CAAC,aAAa,CAAC;CAChD;AAkBD;;;;;;GAMG;AACH,qBAEa,iBAAiB;IAE5B,SAAS,CAAC,MAAM,EAAG,WAAW,EAAE,CAAC;IAGjC,SAAS,CAAC,SAAS,EAAG,MAAM,CAAC;IAG7B,SAAS,CAAC,cAAc,EAAG,OAAO,CAAC;IAEnC;;OAEG;IACI,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO;IAKlC,GAAG,IAAI,OAAO,CAAC,YAAY,CAAC;YAe3B,MAAM;CAqBrB"}
|