@objectstack/observability 17.0.0 → 17.2.0
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/CHANGELOG.md +316 -0
- package/dist/index.cjs +104 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +140 -12
- package/dist/index.d.ts +140 -12
- package/dist/index.js +102 -12
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Logger } from '@objectstack/spec/contracts';
|
|
1
|
+
import { IHttpServer, Logger } from '@objectstack/spec/contracts';
|
|
2
2
|
export { Logger } from '@objectstack/spec/contracts';
|
|
3
3
|
import { ExecutionContext } from '@objectstack/spec/kernel';
|
|
4
4
|
|
|
@@ -118,26 +118,60 @@ declare const OBSERVABILITY_ERRORS_SERVICE = "observability:errors";
|
|
|
118
118
|
* stay in the deployment repo.
|
|
119
119
|
*/
|
|
120
120
|
declare const SEMCONV: {
|
|
121
|
-
/**
|
|
121
|
+
/**
|
|
122
|
+
* Counter, labels: `method`, `route`, `status`. Emitted by the TRANSPORT
|
|
123
|
+
* through the `IHttpServer.afterResponse` seam (#9835), so it covers
|
|
124
|
+
* every inbound request on the server rather than only the routes the
|
|
125
|
+
* runtime dispatcher registers.
|
|
126
|
+
*/
|
|
122
127
|
readonly httpRequestsTotal: "http_requests_total";
|
|
123
|
-
/** Histogram (ms), labels: `method`, `route`. */
|
|
124
|
-
readonly httpRequestDurationMs: "http_request_duration_ms";
|
|
125
128
|
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
129
|
+
* Histogram (ms), labels: `method`, `route`. Emitted by the TRANSPORT
|
|
130
|
+
* through the same seam (#9834). It measures the REQUEST as the transport
|
|
131
|
+
* sees it — first sight to the response existing, middleware chain and
|
|
132
|
+
* body parse included — not the handler's share of it.
|
|
128
133
|
*/
|
|
129
|
-
readonly
|
|
134
|
+
readonly httpRequestDurationMs: "http_request_duration_ms";
|
|
130
135
|
/** Counter, labels: `adapter` (`local`|`s3`|…), `op` (`get`|`put`|`delete`|`head`), `result` (`ok`|`error`). */
|
|
131
136
|
readonly storageOperationsTotal: "storage_operations_total";
|
|
132
137
|
/** Histogram (ms), labels: `adapter`, `op`. */
|
|
133
138
|
readonly storageOperationDurationMs: "storage_operation_duration_ms";
|
|
134
139
|
/** Counter, labels: `adapter`, `op`, `errorClass`. */
|
|
135
140
|
readonly storageErrorsTotal: "storage_errors_total";
|
|
136
|
-
/**
|
|
141
|
+
/**
|
|
142
|
+
* Counter, labels: `adapter` (`memory`|`redis`), `result` (`hit`|`miss`).
|
|
143
|
+
*
|
|
144
|
+
* ⚠️ A flat zero means "NO CONFIGURED CONSUMER", not "no cache activity",
|
|
145
|
+
* and — unlike the HTTP families above — it is NOT an instrumentation gap.
|
|
146
|
+
* The adapters hold the host's registry and count every call they receive
|
|
147
|
+
* (#9832 wired that; #9951 pins it), so a zero here is TRUE. What it fails
|
|
148
|
+
* to communicate is WHY.
|
|
149
|
+
*
|
|
150
|
+
* The why: nothing consults the `cache` service unconditionally. Every
|
|
151
|
+
* production consumer is a rate-limit / budget counter store, and each is
|
|
152
|
+
* gated on a declaration somebody has to write — better-auth's per-IP
|
|
153
|
+
* counters (`rate_limit_max` / `rate_limit_window_seconds` in auth
|
|
154
|
+
* settings), the dispatcher's inbound limiter and its declarative
|
|
155
|
+
* per-endpoint buckets (an armed `rateLimit` budget; with none declared
|
|
156
|
+
* the dispatcher registers no limiter at all), and the per-number OTP send
|
|
157
|
+
* budget (an SMS send path). A default install declares none of them, so
|
|
158
|
+
* this counter stays at 0 while the server handles traffic normally.
|
|
159
|
+
*
|
|
160
|
+
* ⇒ Read a flat `cache_*` as a question about CONFIGURATION, never as a 0%
|
|
161
|
+
* hit rate or a broken adapter. Before trusting a cache hit-rate panel,
|
|
162
|
+
* confirm at least one consumer above is actually armed.
|
|
163
|
+
*/
|
|
137
164
|
readonly cacheLookupsTotal: "cache_lookups_total";
|
|
138
|
-
/**
|
|
165
|
+
/**
|
|
166
|
+
* Counter, labels: `adapter`, `op` (`set`|`delete`|`clear`). Same
|
|
167
|
+
* "zero = no configured consumer" reading as `cacheLookupsTotal` above.
|
|
168
|
+
*/
|
|
139
169
|
readonly cacheWritesTotal: "cache_writes_total";
|
|
140
|
-
/**
|
|
170
|
+
/**
|
|
171
|
+
* Counter, labels: `adapter`, `op`, `errorClass`. Same "zero = no
|
|
172
|
+
* configured consumer" reading as `cacheLookupsTotal` above — a zero is
|
|
173
|
+
* "nothing was asked of the cache", not "every call succeeded".
|
|
174
|
+
*/
|
|
141
175
|
readonly cacheErrorsTotal: "cache_errors_total";
|
|
142
176
|
/**
|
|
143
177
|
* Counter, labels: `app`, `job`. Incremented when a DECLARED background
|
|
@@ -162,9 +196,103 @@ declare const SEMCONV: {
|
|
|
162
196
|
declare const RUNTIME_METRICS: {
|
|
163
197
|
readonly httpRequestsTotal: "http_requests_total";
|
|
164
198
|
readonly httpRequestDurationMs: "http_request_duration_ms";
|
|
165
|
-
readonly httpRequestErrorsTotal: "http_request_errors_total";
|
|
166
199
|
};
|
|
167
200
|
|
|
201
|
+
/**
|
|
202
|
+
* What an `arm*` call in this module did:
|
|
203
|
+
*
|
|
204
|
+
* - `'armed'` — the emitting observer was registered on this call.
|
|
205
|
+
* - `'already-armed'` — some earlier caller already armed this server for
|
|
206
|
+
* THIS metric family; the seam emits it, and this call registered nothing
|
|
207
|
+
* (first-wins).
|
|
208
|
+
* - `'unsupported'` — the transport does not implement the
|
|
209
|
+
* `IHttpServer.afterResponse` seam; it reports NO HTTP metrics (#9835:
|
|
210
|
+
* zero there means "not instrumented", never "no traffic"), and the
|
|
211
|
+
* caller must decide how to degrade — the runtime dispatcher falls back
|
|
212
|
+
* to instrumenting its own routes.
|
|
213
|
+
*/
|
|
214
|
+
type ArmHttpMetricResult = 'armed' | 'already-armed' | 'unsupported';
|
|
215
|
+
/**
|
|
216
|
+
* The name {@link armHttpRequestCounter} shipped with. Kept as an alias
|
|
217
|
+
* rather than renamed: the export is already in the pending release, and the
|
|
218
|
+
* two families arm through separate entry points anyway.
|
|
219
|
+
*/
|
|
220
|
+
type ArmHttpRequestCounterResult = ArmHttpMetricResult;
|
|
221
|
+
/**
|
|
222
|
+
* Arm `http_requests_total{method,route,status}` on a transport through the
|
|
223
|
+
* `IHttpServer.afterResponse` observation seam (#9835) — AT MOST ONCE per
|
|
224
|
+
* server, whoever calls first.
|
|
225
|
+
*
|
|
226
|
+
* ## Why arming is centralized here
|
|
227
|
+
*
|
|
228
|
+
* The contract's ownership rule says a request must never be double-counted,
|
|
229
|
+
* and two composition layers legitimately hold both a server and a metrics
|
|
230
|
+
* registry: the transport's own hosting plugin (`HonoServerPlugin`, which
|
|
231
|
+
* the 2026-08-18 ruling on #9650 made the counter's home) and the runtime
|
|
232
|
+
* dispatcher (whose `observability.metrics` config is the wiring the docs
|
|
233
|
+
* demonstrate). When a host hands ONE registry to both — the ordinary case —
|
|
234
|
+
* two independently-registered observers would land every request on the
|
|
235
|
+
* same series twice: exactly the #9833 distortion, rebuilt one seam over.
|
|
236
|
+
* Routing every arming through this function makes "exactly one
|
|
237
|
+
* counter-emitting observer per server" structural: the first caller arms
|
|
238
|
+
* (in the shipped composition that is the transport plugin, in Phase 1),
|
|
239
|
+
* every later caller is told the seam already counts.
|
|
240
|
+
*
|
|
241
|
+
* The label shape is pinned by the contract: `route` is the transport's
|
|
242
|
+
* `routePattern` — the registered PATTERN, never the concrete path — and
|
|
243
|
+
* `status` is stringified for the label set. Emission goes through the
|
|
244
|
+
* transport's observer-isolation guarantee, so a throwing registry cannot
|
|
245
|
+
* break a response.
|
|
246
|
+
*
|
|
247
|
+
* @param server - The transport. Pass the RAW registered `http.server`
|
|
248
|
+
* instance, not a wrapper: the latch is per object identity, and a wrapper
|
|
249
|
+
* would both fork the latch and (per #5122) risk erasing the optional
|
|
250
|
+
* member this function feature-detects.
|
|
251
|
+
* @param metrics - The registry the counter lands in. First caller wins; a
|
|
252
|
+
* second registry offered later is NOT added (the contract's one-owner
|
|
253
|
+
* rule), and the result says so.
|
|
254
|
+
*/
|
|
255
|
+
declare function armHttpRequestCounter(server: IHttpServer, metrics: MetricsRegistry): ArmHttpMetricResult;
|
|
256
|
+
/**
|
|
257
|
+
* Arm `http_request_duration_ms{method,route}` on a transport through the
|
|
258
|
+
* `IHttpServer.afterResponse` observation seam — AT MOST ONCE per server,
|
|
259
|
+
* whoever calls first. The duration half of #9834, built on the mechanism
|
|
260
|
+
* #9835 proved out for `http_requests_total`.
|
|
261
|
+
*
|
|
262
|
+
* ## Why the histogram has to move too
|
|
263
|
+
*
|
|
264
|
+
* #9835 moved only the counter, which left the docs' two derived signals
|
|
265
|
+
* inconsistent with each other: 5xx rate saw every inbound surface while p95
|
|
266
|
+
* latency still saw the dispatcher's own routes. An operator reading one
|
|
267
|
+
* dashboard got request volume for `/api/v1/*` beside a latency panel with no
|
|
268
|
+
* series for it — and the worse reading is the p95 that IS drawn, computed
|
|
269
|
+
* from dispatcher routes only and presented as the server's.
|
|
270
|
+
*
|
|
271
|
+
* ## ⚠️ The observation WINDOW changes with the emitter
|
|
272
|
+
*
|
|
273
|
+
* The dispatcher's per-route wrapper timed `await handler(req, res)` — handler
|
|
274
|
+
* latency. This seam times the transport's own `use('*')` around
|
|
275
|
+
* `await next()`, which is what {@link HttpResponseObservation.elapsedMs}
|
|
276
|
+
* means: "from the transport first seeing the request to the response
|
|
277
|
+
* existing". That includes the middleware chain and body parse, so the series
|
|
278
|
+
* can only move UP, never down. It is the number an operator's latency panel
|
|
279
|
+
* should have been showing — the request's latency rather than one layer's
|
|
280
|
+
* share of it — but it is a visible change in an existing series, so it is
|
|
281
|
+
* stated here, in the changeset, and in `docs/OBSERVABILITY.md` rather than
|
|
282
|
+
* left for a dashboard to discover.
|
|
283
|
+
*
|
|
284
|
+
* The label set is unchanged and stays the SEMCONV-declared `{method,route}`:
|
|
285
|
+
* `route` is the transport's `routePattern` (the registered PATTERN, never the
|
|
286
|
+
* concrete path), and no `status` label is added — a histogram split by status
|
|
287
|
+
* is a different series shape than the one the docs tell operators to graph.
|
|
288
|
+
*
|
|
289
|
+
* @param server - The transport. Pass the RAW registered `http.server`
|
|
290
|
+
* instance, not a wrapper — the latch is per object identity, and (per
|
|
291
|
+
* #5122) a wrapper risks erasing the optional member this feature-detects.
|
|
292
|
+
* @param metrics - The registry the histogram lands in. First caller wins.
|
|
293
|
+
*/
|
|
294
|
+
declare function armHttpRequestDurationHistogram(server: IHttpServer, metrics: MetricsRegistry): ArmHttpMetricResult;
|
|
295
|
+
|
|
168
296
|
/**
|
|
169
297
|
* No-op metrics registry — the default. Discards every observation.
|
|
170
298
|
* Production deployments should swap this for a real registry; tests
|
|
@@ -617,4 +745,4 @@ declare function isPerfDisclosurePrivileged(): boolean;
|
|
|
617
745
|
*/
|
|
618
746
|
declare function isPerfDisclosurePrincipal(ec: ExecutionContext | undefined): boolean;
|
|
619
747
|
|
|
620
|
-
export { type CapturedError, ConsoleErrorReporter, ConsoleLogger, ConsoleMetricsRegistry, type ErrorReporter, InMemoryErrorReporter, InMemoryMetricsRegistry, JsonLogger, LOG_LEVELS, type LogLevel, type MetricSample, type MetricsRegistry, NoopErrorReporter, NoopLogger, NoopMetricsRegistry, OBSERVABILITY_ERRORS_SERVICE, OBSERVABILITY_METRICS_SERVICE, type OtlpHttpExporterOptions, OtlpHttpMetricsRegistry, type PerfDisclosureGate, PerfTiming, RUNTIME_METRICS, SEMCONV, type ServerTimingDetail, type ServerTimingMark, allowPerfDisclosure, countServerTiming, currentPerfTiming, formatServerTiming, isPerfDisclosureAllowed, isPerfDisclosurePrincipal, isPerfDisclosurePrivileged, measureServerTiming, perfNow, recordServerTiming, recordServerTimingDetail, runWithPerfDisclosure, runWithPerfTiming, startServerTiming };
|
|
748
|
+
export { type ArmHttpMetricResult, type ArmHttpRequestCounterResult, type CapturedError, ConsoleErrorReporter, ConsoleLogger, ConsoleMetricsRegistry, type ErrorReporter, InMemoryErrorReporter, InMemoryMetricsRegistry, JsonLogger, LOG_LEVELS, type LogLevel, type MetricSample, type MetricsRegistry, NoopErrorReporter, NoopLogger, NoopMetricsRegistry, OBSERVABILITY_ERRORS_SERVICE, OBSERVABILITY_METRICS_SERVICE, type OtlpHttpExporterOptions, OtlpHttpMetricsRegistry, type PerfDisclosureGate, PerfTiming, RUNTIME_METRICS, SEMCONV, type ServerTimingDetail, type ServerTimingMark, allowPerfDisclosure, armHttpRequestCounter, armHttpRequestDurationHistogram, countServerTiming, currentPerfTiming, formatServerTiming, isPerfDisclosureAllowed, isPerfDisclosurePrincipal, isPerfDisclosurePrivileged, measureServerTiming, perfNow, recordServerTiming, recordServerTimingDetail, runWithPerfDisclosure, runWithPerfTiming, startServerTiming };
|
package/dist/index.js
CHANGED
|
@@ -4,16 +4,35 @@ var OBSERVABILITY_ERRORS_SERVICE = "observability:errors";
|
|
|
4
4
|
|
|
5
5
|
// src/semconv.ts
|
|
6
6
|
var SEMCONV = {
|
|
7
|
-
// ── HTTP —
|
|
8
|
-
/**
|
|
7
|
+
// ── HTTP — emitter differs per family, see each ────────────────────
|
|
8
|
+
/**
|
|
9
|
+
* Counter, labels: `method`, `route`, `status`. Emitted by the TRANSPORT
|
|
10
|
+
* through the `IHttpServer.afterResponse` seam (#9835), so it covers
|
|
11
|
+
* every inbound request on the server rather than only the routes the
|
|
12
|
+
* runtime dispatcher registers.
|
|
13
|
+
*/
|
|
9
14
|
httpRequestsTotal: "http_requests_total",
|
|
10
|
-
/** Histogram (ms), labels: `method`, `route`. */
|
|
11
|
-
httpRequestDurationMs: "http_request_duration_ms",
|
|
12
15
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
16
|
+
* Histogram (ms), labels: `method`, `route`. Emitted by the TRANSPORT
|
|
17
|
+
* through the same seam (#9834). It measures the REQUEST as the transport
|
|
18
|
+
* sees it — first sight to the response existing, middleware chain and
|
|
19
|
+
* body parse included — not the handler's share of it.
|
|
15
20
|
*/
|
|
16
|
-
|
|
21
|
+
httpRequestDurationMs: "http_request_duration_ms",
|
|
22
|
+
// ⛔ RETIRED — `http_request_errors_total` was removed in
|
|
23
|
+
// `@objectstack/observability` 17.2.0 (#9834, ADR-0049 enforce-or-remove).
|
|
24
|
+
// ⛔ Do not re-add the name. It was DECLARED here as a stable server-wide
|
|
25
|
+
// signal and EMITTED only from `@objectstack/runtime`'s per-route wrapper,
|
|
26
|
+
// on a THROWN handler — so it never saw auth's `getRawApp()` mount, the
|
|
27
|
+
// REST data API, or any error a handler answered politely through
|
|
28
|
+
// `errorResponseBase` (which sets a status and does not re-throw). No
|
|
29
|
+
// transport-side emitter could preserve that population either: the
|
|
30
|
+
// `IHttpServer.afterResponse` observation carries `{method, routePattern,
|
|
31
|
+
// status, elapsedMs}` and no throw signal at all.
|
|
32
|
+
// ⇒ Read the 5xx rate from `http_requests_total{status=~"5.."}` instead.
|
|
33
|
+
// The transport emits that family through the seam, so it covers every
|
|
34
|
+
// inbound surface (#9650 / #9835 / #10004) and carries the status label
|
|
35
|
+
// this counter only stood in for. Maintainer ruling 2026-08-20.
|
|
17
36
|
// ── Storage — emitted by `@objectstack/service-storage` adapters ──
|
|
18
37
|
/** Counter, labels: `adapter` (`local`|`s3`|…), `op` (`get`|`put`|`delete`|`head`), `result` (`ok`|`error`). */
|
|
19
38
|
storageOperationsTotal: "storage_operations_total",
|
|
@@ -22,11 +41,41 @@ var SEMCONV = {
|
|
|
22
41
|
/** Counter, labels: `adapter`, `op`, `errorClass`. */
|
|
23
42
|
storageErrorsTotal: "storage_errors_total",
|
|
24
43
|
// ── Cache — emitted by `@objectstack/service-cache` adapters ──
|
|
25
|
-
|
|
44
|
+
// Uniform emitter; what varies is who CONSULTS the service — see below.
|
|
45
|
+
/**
|
|
46
|
+
* Counter, labels: `adapter` (`memory`|`redis`), `result` (`hit`|`miss`).
|
|
47
|
+
*
|
|
48
|
+
* ⚠️ A flat zero means "NO CONFIGURED CONSUMER", not "no cache activity",
|
|
49
|
+
* and — unlike the HTTP families above — it is NOT an instrumentation gap.
|
|
50
|
+
* The adapters hold the host's registry and count every call they receive
|
|
51
|
+
* (#9832 wired that; #9951 pins it), so a zero here is TRUE. What it fails
|
|
52
|
+
* to communicate is WHY.
|
|
53
|
+
*
|
|
54
|
+
* The why: nothing consults the `cache` service unconditionally. Every
|
|
55
|
+
* production consumer is a rate-limit / budget counter store, and each is
|
|
56
|
+
* gated on a declaration somebody has to write — better-auth's per-IP
|
|
57
|
+
* counters (`rate_limit_max` / `rate_limit_window_seconds` in auth
|
|
58
|
+
* settings), the dispatcher's inbound limiter and its declarative
|
|
59
|
+
* per-endpoint buckets (an armed `rateLimit` budget; with none declared
|
|
60
|
+
* the dispatcher registers no limiter at all), and the per-number OTP send
|
|
61
|
+
* budget (an SMS send path). A default install declares none of them, so
|
|
62
|
+
* this counter stays at 0 while the server handles traffic normally.
|
|
63
|
+
*
|
|
64
|
+
* ⇒ Read a flat `cache_*` as a question about CONFIGURATION, never as a 0%
|
|
65
|
+
* hit rate or a broken adapter. Before trusting a cache hit-rate panel,
|
|
66
|
+
* confirm at least one consumer above is actually armed.
|
|
67
|
+
*/
|
|
26
68
|
cacheLookupsTotal: "cache_lookups_total",
|
|
27
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* Counter, labels: `adapter`, `op` (`set`|`delete`|`clear`). Same
|
|
71
|
+
* "zero = no configured consumer" reading as `cacheLookupsTotal` above.
|
|
72
|
+
*/
|
|
28
73
|
cacheWritesTotal: "cache_writes_total",
|
|
29
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Counter, labels: `adapter`, `op`, `errorClass`. Same "zero = no
|
|
76
|
+
* configured consumer" reading as `cacheLookupsTotal` above — a zero is
|
|
77
|
+
* "nothing was asked of the cache", not "every call succeeded".
|
|
78
|
+
*/
|
|
30
79
|
cacheErrorsTotal: "cache_errors_total",
|
|
31
80
|
// ── Background jobs — emitted by `@objectstack/runtime`'s AppPlugin ──
|
|
32
81
|
/**
|
|
@@ -46,10 +95,49 @@ var SEMCONV = {
|
|
|
46
95
|
};
|
|
47
96
|
var RUNTIME_METRICS = {
|
|
48
97
|
httpRequestsTotal: SEMCONV.httpRequestsTotal,
|
|
49
|
-
httpRequestDurationMs: SEMCONV.httpRequestDurationMs
|
|
50
|
-
httpRequestErrorsTotal
|
|
98
|
+
httpRequestDurationMs: SEMCONV.httpRequestDurationMs
|
|
99
|
+
// `httpRequestErrorsTotal` retired with its SEMCONV declaration above
|
|
100
|
+
// (#9834). The alias is not a compatibility window of its own: there is no
|
|
101
|
+
// emitter left to read, so keeping the name here would hand callers a
|
|
102
|
+
// string nothing ever writes.
|
|
51
103
|
};
|
|
52
104
|
|
|
105
|
+
// src/http-transport-metrics.ts
|
|
106
|
+
var HTTP_REQUEST_COUNTER_ARMED = /* @__PURE__ */ Symbol.for(
|
|
107
|
+
"objectstack.observability.httpRequestCounterArmed"
|
|
108
|
+
);
|
|
109
|
+
function armHttpRequestCounter(server, metrics) {
|
|
110
|
+
if (typeof server.afterResponse !== "function") return "unsupported";
|
|
111
|
+
const latched = server;
|
|
112
|
+
if (latched[HTTP_REQUEST_COUNTER_ARMED]) return "already-armed";
|
|
113
|
+
latched[HTTP_REQUEST_COUNTER_ARMED] = true;
|
|
114
|
+
server.afterResponse((observation) => {
|
|
115
|
+
metrics.counter(RUNTIME_METRICS.httpRequestsTotal, {
|
|
116
|
+
method: observation.method,
|
|
117
|
+
route: observation.routePattern,
|
|
118
|
+
status: String(observation.status)
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
return "armed";
|
|
122
|
+
}
|
|
123
|
+
var HTTP_REQUEST_DURATION_ARMED = /* @__PURE__ */ Symbol.for(
|
|
124
|
+
"objectstack.observability.httpRequestDurationArmed"
|
|
125
|
+
);
|
|
126
|
+
function armHttpRequestDurationHistogram(server, metrics) {
|
|
127
|
+
if (typeof server.afterResponse !== "function") return "unsupported";
|
|
128
|
+
const latched = server;
|
|
129
|
+
if (latched[HTTP_REQUEST_DURATION_ARMED]) return "already-armed";
|
|
130
|
+
latched[HTTP_REQUEST_DURATION_ARMED] = true;
|
|
131
|
+
server.afterResponse((observation) => {
|
|
132
|
+
metrics.histogram(
|
|
133
|
+
RUNTIME_METRICS.httpRequestDurationMs,
|
|
134
|
+
observation.elapsedMs,
|
|
135
|
+
{ method: observation.method, route: observation.routePattern }
|
|
136
|
+
);
|
|
137
|
+
});
|
|
138
|
+
return "armed";
|
|
139
|
+
}
|
|
140
|
+
|
|
53
141
|
// src/metrics-exporters.ts
|
|
54
142
|
var NoopMetricsRegistry = class {
|
|
55
143
|
counter() {
|
|
@@ -648,6 +736,8 @@ export {
|
|
|
648
736
|
RUNTIME_METRICS,
|
|
649
737
|
SEMCONV,
|
|
650
738
|
allowPerfDisclosure,
|
|
739
|
+
armHttpRequestCounter,
|
|
740
|
+
armHttpRequestDurationHistogram,
|
|
651
741
|
countServerTiming,
|
|
652
742
|
currentPerfTiming,
|
|
653
743
|
formatServerTiming,
|