@nodii/telemetry 0.12.0 → 0.13.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/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/lambda/index.d.ts +77 -0
- package/dist/lambda/index.d.ts.map +1 -0
- package/dist/lambda/index.js +130 -0
- package/dist/lambda/index.js.map +1 -0
- package/package.json +5 -1
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6BA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EACV,WAAW,EACX,aAAa,EACb,OAAO,EACP,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,YAAY,EACV,oBAAoB,EACpB,IAAI,EACJ,QAAQ,EACR,WAAW,EACX,UAAU,EACV,UAAU,EACV,UAAU,GACX,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5E,YAAY,EACV,aAAa,EACb,WAAW,EACX,eAAe,EACf,UAAU,EACV,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,wBAAwB,EACxB,gCAAgC,EAChC,yBAAyB,EACzB,cAAc,EACd,eAAe,EACf,8BAA8B,EAC9B,KAAK,EACL,iBAAiB,EACjB,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,SAAS,EACT,UAAU,EACV,SAAS,EACT,iBAAiB,EACjB,UAAU,EACV,aAAa,EACb,YAAY,EACZ,WAAW,EACX,0BAA0B,GAC3B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,YAAY,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,UAAU,EACV,cAAc,EACd,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,cAAc,EACd,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AACrD,YAAY,EACV,SAAS,EACT,sBAAsB,EACtB,oBAAoB,EACpB,yBAAyB,EACzB,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,EACxB,yBAAyB,EACzB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,qBAAqB,EACrB,wBAAwB,EACxB,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,uBAAuB,EACvB,eAAe,EACf,4BAA4B,EAC5B,2BAA2B,GAC5B,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
// that SET NodiiContext at the entry seam.
|
|
9
9
|
// - @nodii/telemetry/outbox — Outbox emit + propagation-column adapter.
|
|
10
10
|
// - @nodii/telemetry/drizzle — Drizzle audit_event schema fragments.
|
|
11
|
+
// - @nodii/telemetry/lambda — AWS Lambda lifecycle wrapper (C4 lock):
|
|
12
|
+
// cold-start init at module-load +
|
|
13
|
+
// force-flush-on-return so spans/metrics/
|
|
14
|
+
// logs survive the post-handler freeze.
|
|
15
|
+
// First consumer: s2s-auth-service.
|
|
11
16
|
//
|
|
12
17
|
// Not yet shipped (D44 § 6.5 lists them; no implementation exists yet —
|
|
13
18
|
// they are tracked as follow-on requests, NOT advertised as vapor):
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,EAAE;AACF,0EAA0E;AAC1E,EAAE;AACF,0EAA0E;AAC1E,yEAAyE;AACzE,4EAA4E;AAC5E,2EAA2E;AAC3E,4EAA4E;AAC5E,wEAAwE;AACxE,EAAE;AACF,wEAAwE;AACxE,oEAAoE;AACpE,uEAAuE;AACvE,8DAA8D;AAC9D,mDAAmD;AACnD,EAAE;AACF,oEAAoE;AACpE,gBAAgB;AAChB,EAAE;AACF,oEAAoE;AACpE,gEAAgE;AAChE,0DAA0D;AAE1D,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAOnD,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAWjD,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAS5E,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,wBAAwB,EACxB,gCAAgC,EAChC,yBAAyB,EACzB,cAAc,EACd,eAAe,EACf,8BAA8B,EAC9B,KAAK,EACL,iBAAiB,EACjB,cAAc,GACf,MAAM,YAAY,CAAC;AAYpB,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD,OAAO,EAAE,MAAM,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,UAAU,EACV,cAAc,EACd,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,cAAc,EACd,eAAe,GAChB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AA0BrD,OAAO,EACL,uBAAuB,EACvB,eAAe,EACf,4BAA4B,EAC5B,2BAA2B,GAC5B,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,EAAE;AACF,0EAA0E;AAC1E,EAAE;AACF,0EAA0E;AAC1E,yEAAyE;AACzE,4EAA4E;AAC5E,2EAA2E;AAC3E,4EAA4E;AAC5E,wEAAwE;AACxE,0EAA0E;AAC1E,mEAAmE;AACnE,0EAA0E;AAC1E,wEAAwE;AACxE,oEAAoE;AACpE,EAAE;AACF,wEAAwE;AACxE,oEAAoE;AACpE,uEAAuE;AACvE,8DAA8D;AAC9D,mDAAmD;AACnD,EAAE;AACF,oEAAoE;AACpE,gBAAgB;AAChB,EAAE;AACF,oEAAoE;AACpE,gEAAgE;AAChE,0DAA0D;AAE1D,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAOnD,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAWjD,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAS5E,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,wBAAwB,EACxB,gCAAgC,EAChC,yBAAyB,EACzB,cAAc,EACd,eAAe,EACf,8BAA8B,EAC9B,KAAK,EACL,iBAAiB,EACjB,cAAc,GACf,MAAM,YAAY,CAAC;AAYpB,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD,OAAO,EAAE,MAAM,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,UAAU,EACV,cAAc,EACd,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,cAAc,EACd,eAAe,GAChB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AA0BrD,OAAO,EACL,uBAAuB,EACvB,eAAe,EACf,4BAA4B,EAC5B,2BAA2B,GAC5B,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { InitTelemetryConfig, MetricFamily } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* A minimal structural AWS Lambda handler. We intentionally do NOT depend
|
|
4
|
+
* on `aws-lambda` types — the wrapper is event-shape agnostic so it works
|
|
5
|
+
* for an APIGW-proxied Hono app (`handle(app)` from `hono/aws-lambda`), a
|
|
6
|
+
* raw SQS/SNS/EventBridge handler, or any other `async (event, context)`
|
|
7
|
+
* function. `Event` / `Result` are inferred from the wrapped handler.
|
|
8
|
+
*/
|
|
9
|
+
export type LambdaHandler<Event = unknown, Result = unknown> = (event: Event, context?: unknown) => Promise<Result> | Result;
|
|
10
|
+
/** Config for the cold-start counter tick (C4: `s2s.cold_start_total`). */
|
|
11
|
+
export interface ColdStartMetricConfig {
|
|
12
|
+
/**
|
|
13
|
+
* The vocabulary-registered counter name to increment once per cold
|
|
14
|
+
* boot (e.g. `"s2s.cold_start_total"`). MUST be registered by the
|
|
15
|
+
* consumer via `initTelemetry({ vocabularyRegistry })` — the name is
|
|
16
|
+
* service-specific, so the wrapper cannot hardcode it.
|
|
17
|
+
*
|
|
18
|
+
* If the name is NOT registered (vocabulary miss), the increment is a
|
|
19
|
+
* caught no-op + a one-line warn — a metric mis-config must NOT crash
|
|
20
|
+
* module-load (R1 / pillar 5).
|
|
21
|
+
*/
|
|
22
|
+
name: string;
|
|
23
|
+
/** The metric family for the counter. Defaults to `"request_handler"`. */
|
|
24
|
+
family?: MetricFamily;
|
|
25
|
+
/** Static labels stamped on the cold-start tick. */
|
|
26
|
+
labels?: Record<string, string | number>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* `initLambdaTelemetry({...})` argument shape. Extends the base
|
|
30
|
+
* `initTelemetry` config (serviceName + env + otlp + vocabulary, …) with
|
|
31
|
+
* the Lambda-specific cold-start knob.
|
|
32
|
+
*/
|
|
33
|
+
export interface InitLambdaTelemetryConfig extends InitTelemetryConfig {
|
|
34
|
+
/**
|
|
35
|
+
* Counter to increment once per cold boot. Omit to skip the cold-start
|
|
36
|
+
* tick entirely (some services track cold starts via a log line only).
|
|
37
|
+
*/
|
|
38
|
+
coldStartMetric?: ColdStartMetricConfig;
|
|
39
|
+
/**
|
|
40
|
+
* Name of the per-invocation request span opened by `wrap`. Defaults to
|
|
41
|
+
* `"<serviceName>.lambda.invoke"`.
|
|
42
|
+
*/
|
|
43
|
+
requestSpanName?: string;
|
|
44
|
+
}
|
|
45
|
+
/** The handle returned by `initLambdaTelemetry`. */
|
|
46
|
+
export interface LambdaTelemetry {
|
|
47
|
+
/**
|
|
48
|
+
* Wrap a Lambda handler so every invocation:
|
|
49
|
+
* 1. runs inside a request span (`requestSpanName`);
|
|
50
|
+
* 2. force-flushes the trace + log + metric providers BEFORE the
|
|
51
|
+
* handler's result returns (success AND error paths), so nothing
|
|
52
|
+
* is lost when Lambda freezes;
|
|
53
|
+
* 3. is RESILIENT — a flush failure is caught + logged and never
|
|
54
|
+
* propagates; the handler's own result / error is what returns.
|
|
55
|
+
*/
|
|
56
|
+
wrap<Event, Result>(handler: LambdaHandler<Event, Result>): (event: Event, context?: unknown) => Promise<Result>;
|
|
57
|
+
/**
|
|
58
|
+
* Force-flush the active OTLP pipelines (trace + log + metric). No-op
|
|
59
|
+
* when no OTLP adapter is installed (e.g. `disableOtlp` / no endpoint).
|
|
60
|
+
* Exposed for callers that flush at custom lifecycle points.
|
|
61
|
+
*/
|
|
62
|
+
forceFlush(): Promise<void>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Boot telemetry for a Lambda at MODULE LOAD (per C4). Call this once at
|
|
66
|
+
* the top of the handler module — not per invocation. Returns a
|
|
67
|
+
* `LambdaTelemetry` handle whose `wrap` adds the per-invocation request
|
|
68
|
+
* span + force-flush-on-return.
|
|
69
|
+
*
|
|
70
|
+
* Idempotency: `initTelemetry` is itself idempotent (a second call
|
|
71
|
+
* warn-no-ops), so a doubly-imported / re-required handler module does
|
|
72
|
+
* NOT double-configure the SDK. The cold-start tick is likewise fired
|
|
73
|
+
* only on the call that actually performs init (the first one) — a second
|
|
74
|
+
* `initLambdaTelemetry` will not re-tick, matching "once per cold boot".
|
|
75
|
+
*/
|
|
76
|
+
export declare function initLambdaTelemetry(config: InitLambdaTelemetryConfig): LambdaTelemetry;
|
|
77
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lambda/index.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAErE;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,CAAC,KAAK,GAAG,OAAO,EAAE,MAAM,GAAG,OAAO,IAAI,CAC7D,KAAK,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,OAAO,KACd,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;AAE9B,2EAA2E;AAC3E,MAAM,WAAW,qBAAqB;IACpC;;;;;;;;;OASG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;CAC1C;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAA0B,SAAQ,mBAAmB;IACpE;;;OAGG;IACH,eAAe,CAAC,EAAE,qBAAqB,CAAC;IACxC;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B;;;;;;;;OAQG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,EAChB,OAAO,EAAE,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,GACpC,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACxD;;;;OAIG;IACH,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAyBD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,yBAAyB,GAChC,eAAe,CAiDjB"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// `@nodii/telemetry/lambda` — the Lambda lifecycle wrapper.
|
|
2
|
+
//
|
|
3
|
+
// Spec: C4 lock — `services/s2s-auth-service/features/telemetry-instrumentation.md`
|
|
4
|
+
// § "The `@nodii/telemetry/lambda` sub-package" + `nodii-libs/telemetry.md`
|
|
5
|
+
// § 10 (subpath exports). First consumer: s2s-auth-service (Lambda-shaped).
|
|
6
|
+
//
|
|
7
|
+
// AWS Lambda freezes the execution environment the instant a handler
|
|
8
|
+
// returns. The OTel batch processors buffer spans / logs / metrics and
|
|
9
|
+
// flush them on a timer — so on a Lambda that timer rarely fires before
|
|
10
|
+
// the freeze, and signals are LOST. Two lifecycle hooks fix this, both
|
|
11
|
+
// locked by C4:
|
|
12
|
+
//
|
|
13
|
+
// 1. Cold-start init at module-load — `initLambdaTelemetry({...})` is
|
|
14
|
+
// called once at the top of the handler module (NOT per-invocation).
|
|
15
|
+
// It boots the SDK (idempotent — a second call warn-no-ops, see
|
|
16
|
+
// `initTelemetry`) and ticks a cold-start counter exactly once per
|
|
17
|
+
// cold boot.
|
|
18
|
+
//
|
|
19
|
+
// 2. `forceFlush()` before the handler returns — `handle.wrap(fn)`
|
|
20
|
+
// runs the handler inside a request span and `await`s a force-flush
|
|
21
|
+
// of the trace + log + metric providers BEFORE returning (and on the
|
|
22
|
+
// error path), so nothing is lost when the environment freezes.
|
|
23
|
+
//
|
|
24
|
+
// RESILIENCE (telemetry-doctrine pillar 5 / R1 — dependency-down path):
|
|
25
|
+
// a force-flush FAILURE (collector down / OTLP timeout) MUST NOT break
|
|
26
|
+
// the Lambda. A flaky telemetry backend cannot be allowed to take down
|
|
27
|
+
// the function. `wrap` therefore catches + logs any flush rejection and
|
|
28
|
+
// STILL returns the handler's result (or re-throws the handler's own
|
|
29
|
+
// error). The only thing that propagates out of the wrapper is the
|
|
30
|
+
// handler's own outcome — never a telemetry-plumbing failure.
|
|
31
|
+
//
|
|
32
|
+
// Lives behind a subpath so non-Lambda services never pull this lifecycle
|
|
33
|
+
// glue into their bundle.
|
|
34
|
+
import { getTelemetryOrNull, initTelemetry } from "../init.js";
|
|
35
|
+
import { metrics } from "../metrics.js";
|
|
36
|
+
import { forceFlush as flushOtlpPipelines } from "../otlp-adapter.js";
|
|
37
|
+
import { tracer } from "../tracer.js";
|
|
38
|
+
/**
|
|
39
|
+
* Increment the cold-start counter exactly once, defensively. A
|
|
40
|
+
* vocabulary miss (name not registered) or any other metric error is
|
|
41
|
+
* caught + warn-logged — a cold-start metric mis-config must NOT crash
|
|
42
|
+
* the Lambda at module load (R1 / pillar 5).
|
|
43
|
+
*/
|
|
44
|
+
function tickColdStart(cfg) {
|
|
45
|
+
try {
|
|
46
|
+
const counter = metrics.counter(cfg.name, {
|
|
47
|
+
family: cfg.family ?? "request_handler",
|
|
48
|
+
description: "Lambda cold-start counter (incremented once per cold boot)",
|
|
49
|
+
labelKeys: Object.keys(cfg.labels ?? {}),
|
|
50
|
+
});
|
|
51
|
+
counter.inc(cfg.labels ?? {});
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
const cause = err instanceof Error ? err.message : String(err);
|
|
55
|
+
// eslint-disable-next-line no-console
|
|
56
|
+
console.warn(`[@nodii/telemetry/lambda] cold-start counter "${cfg.name}" could not be incremented (is it in the vocabularyRegistry?). Continuing — a metric mis-config must not crash module-load. Cause: ${cause}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Boot telemetry for a Lambda at MODULE LOAD (per C4). Call this once at
|
|
61
|
+
* the top of the handler module — not per invocation. Returns a
|
|
62
|
+
* `LambdaTelemetry` handle whose `wrap` adds the per-invocation request
|
|
63
|
+
* span + force-flush-on-return.
|
|
64
|
+
*
|
|
65
|
+
* Idempotency: `initTelemetry` is itself idempotent (a second call
|
|
66
|
+
* warn-no-ops), so a doubly-imported / re-required handler module does
|
|
67
|
+
* NOT double-configure the SDK. The cold-start tick is likewise fired
|
|
68
|
+
* only on the call that actually performs init (the first one) — a second
|
|
69
|
+
* `initLambdaTelemetry` will not re-tick, matching "once per cold boot".
|
|
70
|
+
*/
|
|
71
|
+
export function initLambdaTelemetry(config) {
|
|
72
|
+
// Was the SDK already initialized before this call? If so, this is a
|
|
73
|
+
// redundant init (re-imported module / double-call) and we must NOT
|
|
74
|
+
// re-tick the cold-start counter — "once per cold boot".
|
|
75
|
+
const alreadyInitialized = getTelemetryOrNull() !== null;
|
|
76
|
+
// `initTelemetry` consumes only the base config keys; the extra
|
|
77
|
+
// Lambda-specific keys are ignored by it (excess-property tolerant at
|
|
78
|
+
// runtime). It is idempotent — a second call warn-no-ops.
|
|
79
|
+
initTelemetry(config);
|
|
80
|
+
if (!alreadyInitialized && config.coldStartMetric) {
|
|
81
|
+
tickColdStart(config.coldStartMetric);
|
|
82
|
+
}
|
|
83
|
+
const spanName = config.requestSpanName ?? `${config.serviceName}.lambda.invoke`;
|
|
84
|
+
// The handle is built first so `wrap` can route its flush through
|
|
85
|
+
// `handle.forceFlush` — that is the single flush seam (and the one a
|
|
86
|
+
// test spies on to fault-inject a flush failure).
|
|
87
|
+
const handle = {
|
|
88
|
+
async forceFlush() {
|
|
89
|
+
await flushOtlpPipelines();
|
|
90
|
+
},
|
|
91
|
+
wrap(handler) {
|
|
92
|
+
return async (event, context) => {
|
|
93
|
+
try {
|
|
94
|
+
// Run the handler inside a request span. `tracer.span` opens +
|
|
95
|
+
// closes the span and applies D27 status mapping.
|
|
96
|
+
const result = await tracer.span(spanName, () => Promise.resolve(handler(event, context)));
|
|
97
|
+
// Flush BEFORE returning — but a flush failure must NOT mask a
|
|
98
|
+
// successful handler result.
|
|
99
|
+
await safeFlush(handle);
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
catch (handlerErr) {
|
|
103
|
+
// The handler itself threw. Still flush (so the error span +
|
|
104
|
+
// partial signals aren't lost on freeze) — non-fatally — then
|
|
105
|
+
// re-throw the handler's OWN error (NOT a flush error).
|
|
106
|
+
await safeFlush(handle);
|
|
107
|
+
throw handlerErr;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
return handle;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Force-flush via `handle.forceFlush` but NEVER throw. A flush failure
|
|
116
|
+
* (collector down / OTLP timeout) is caught + warn-logged; the Lambda
|
|
117
|
+
* result must not be lost to a flaky telemetry backend (pillar 5 / R1
|
|
118
|
+
* dependency-down path).
|
|
119
|
+
*/
|
|
120
|
+
async function safeFlush(handle) {
|
|
121
|
+
try {
|
|
122
|
+
await handle.forceFlush();
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
const cause = err instanceof Error ? err.message : String(err);
|
|
126
|
+
// eslint-disable-next-line no-console
|
|
127
|
+
console.warn(`[@nodii/telemetry/lambda] forceFlush() failed before return (telemetry backend down?). Swallowed — the handler result is returned regardless. Cause: ${cause}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lambda/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodii/telemetry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Substrate observability library for the Nodii microservice stack — OTel wrapper, log envelope, intent lifecycle, audit signal, agent registry, context propagation. Spec: planning hub docKey=telemetry.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
"./outbox": {
|
|
23
23
|
"types": "./dist/outbox/index.d.ts",
|
|
24
24
|
"default": "./dist/outbox/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./lambda": {
|
|
27
|
+
"types": "./dist/lambda/index.d.ts",
|
|
28
|
+
"default": "./dist/lambda/index.js"
|
|
25
29
|
}
|
|
26
30
|
},
|
|
27
31
|
"files": [
|