@bymax-one/nest-logger 1.1.0 → 1.2.1
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 +446 -1
- package/README.md +381 -88
- package/dist/server/index.cjs +891 -145
- package/dist/server/index.d.cts +361 -51
- package/dist/server/index.d.ts +361 -51
- package/dist/server/index.mjs +896 -150
- package/dist/shared/index.cjs +15 -1
- package/dist/shared/index.d.cts +149 -8
- package/dist/shared/index.d.ts +149 -8
- package/dist/shared/index.mjs +15 -2
- package/package.json +1 -1
package/dist/server/index.d.cts
CHANGED
|
@@ -96,6 +96,79 @@ interface ServiceMetadata {
|
|
|
96
96
|
readonly name: string;
|
|
97
97
|
/** OTel attribute `service.version` — semver, commit SHA, or build tag. */
|
|
98
98
|
readonly version: string;
|
|
99
|
+
/**
|
|
100
|
+
* OTel attribute `service.namespace` (**Stable**) — the group the service
|
|
101
|
+
* belongs to, e.g. a team, a bounded context or a tenant of the platform.
|
|
102
|
+
* Two services may share a `name` as long as their namespaces differ.
|
|
103
|
+
*/
|
|
104
|
+
readonly namespace?: string;
|
|
105
|
+
/**
|
|
106
|
+
* OTel attribute `service.instance.id` (**Stable**) — identifies ONE running
|
|
107
|
+
* instance, so the triplet (namespace, name, instanceId) is globally unique.
|
|
108
|
+
*
|
|
109
|
+
* The library never generates this. A value minted here would be the logger's
|
|
110
|
+
* rather than the OpenTelemetry Resource's, so logs and traces would disagree
|
|
111
|
+
* about which instance served a request, and it would change on every restart
|
|
112
|
+
* while looking authoritative. Supply it from the platform — the Kubernetes
|
|
113
|
+
* pod UID, the ECS task ARN, the VM instance id — or through
|
|
114
|
+
* `OTEL_RESOURCE_ATTRIBUTES`, which is the same source the SDK reads.
|
|
115
|
+
*
|
|
116
|
+
* Treat it as HIGH cardinality: fine as a log field, not as a metric label.
|
|
117
|
+
*/
|
|
118
|
+
readonly instanceId?: string;
|
|
119
|
+
/**
|
|
120
|
+
* OTel attribute `deployment.environment.name` (**Stable**) — `production`,
|
|
121
|
+
* `staging`, and so on. Falls back to `NODE_ENV` when omitted.
|
|
122
|
+
*
|
|
123
|
+
* The older `deployment.environment` spelling is **Deprecated** in the
|
|
124
|
+
* semantic conventions and is never emitted.
|
|
125
|
+
*/
|
|
126
|
+
readonly environment?: string;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* The `service` block as it appears in an EMITTED record, under the default
|
|
130
|
+
* `resourceFormat: 'nested'`.
|
|
131
|
+
*
|
|
132
|
+
* Distinct from {@link ServiceMetadata} on purpose, and the distinction is the
|
|
133
|
+
* kind of defect this library has already been bitten by once: a published type
|
|
134
|
+
* that describes something the runtime never emits. `ServiceMetadata` is what a
|
|
135
|
+
* consumer CONFIGURES — flat option names like `instanceId` and `environment`.
|
|
136
|
+
* What comes out is the flattened OTel attribute path: `service.instance.id`
|
|
137
|
+
* nests as `service: { instance: { id } }`, and the environment is not under
|
|
138
|
+
* `service` at all, it is `deployment.environment.name`. Typing the emitted
|
|
139
|
+
* record with the configuration interface would advertise `entry.service.instanceId`
|
|
140
|
+
* to every consumer, and that property does not exist on any entry.
|
|
141
|
+
*
|
|
142
|
+
* Under `resourceFormat: 'flat'` these values appear as dotted top-level keys
|
|
143
|
+
* (`"service.instance.id"`), which {@link LogEntry}'s index signature already
|
|
144
|
+
* admits — a flat record simply has no `service` object.
|
|
145
|
+
*/
|
|
146
|
+
interface EmittedServiceResource {
|
|
147
|
+
/** OTel `service.name`. */
|
|
148
|
+
readonly name: string;
|
|
149
|
+
/** OTel `service.version`. */
|
|
150
|
+
readonly version: string;
|
|
151
|
+
/** OTel `service.namespace`, when resolved. */
|
|
152
|
+
readonly namespace?: string;
|
|
153
|
+
/** OTel `service.instance.id`, nested as the attribute path spells it. */
|
|
154
|
+
readonly instance?: {
|
|
155
|
+
readonly id: string;
|
|
156
|
+
};
|
|
157
|
+
/** Consumer-supplied extras carried through from the `service` option. */
|
|
158
|
+
readonly [key: string]: unknown;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* The `deployment` block as it appears in an EMITTED record under
|
|
162
|
+
* `resourceFormat: 'nested'`, carrying `deployment.environment.name`.
|
|
163
|
+
*
|
|
164
|
+
* Absent entirely when no source resolved an environment. The deprecated
|
|
165
|
+
* `deployment.environment` spelling is never emitted.
|
|
166
|
+
*/
|
|
167
|
+
interface EmittedDeploymentResource {
|
|
168
|
+
/** OTel `deployment.environment.name`, flattened from its attribute path. */
|
|
169
|
+
readonly environment: {
|
|
170
|
+
readonly name: string;
|
|
171
|
+
};
|
|
99
172
|
}
|
|
100
173
|
|
|
101
174
|
/**
|
|
@@ -174,8 +247,69 @@ interface BymaxLoggerModuleOptions {
|
|
|
174
247
|
* `BymaxLoggerModule.useNestLogger(app)` in `main.ts` instead. Default: true.
|
|
175
248
|
*/
|
|
176
249
|
shouldUseAsNestLogger?: boolean;
|
|
177
|
-
/**
|
|
250
|
+
/**
|
|
251
|
+
* Additional `fast-redact` paths, applied on top of the default coverage
|
|
252
|
+
* whatever `redactStrategy` is in force.
|
|
253
|
+
*/
|
|
178
254
|
redactPaths?: readonly string[];
|
|
255
|
+
/**
|
|
256
|
+
* Engine backing the DEFAULT redaction set (never the consumer's own
|
|
257
|
+
* `redactPaths`, which are always `fast-redact` paths).
|
|
258
|
+
*
|
|
259
|
+
* - `'names'` (default) — one recursive walk censoring any value whose KEY
|
|
260
|
+
* NAME is in `REDACT_COMMON_FIELDS`, at ANY depth. ~943 k logs/s.
|
|
261
|
+
* - `'paths'` — the pre-1.2 engine: `DEFAULT_REDACT_PATHS` handed to
|
|
262
|
+
* `fast-redact`. Matches only wildcard depths 1–4 and measures ~9.3 k
|
|
263
|
+
* logs/s. Provided as an escape hatch for a consumer depending on exact
|
|
264
|
+
* path-matching semantics; expect it to be removed in a future major.
|
|
265
|
+
*/
|
|
266
|
+
redactStrategy?: 'names' | 'paths';
|
|
267
|
+
/**
|
|
268
|
+
* Shape of the resource/service identity on every entry. Default `'nested'`.
|
|
269
|
+
*
|
|
270
|
+
* - `'nested'` — `{ service: { name, version, namespace, instance: { id } },
|
|
271
|
+
* deployment: { environment: { name } } }`. The historical shape, extended;
|
|
272
|
+
* no existing query breaks.
|
|
273
|
+
* - `'flat'` — the dotted OTel attribute names verbatim (`service.name`,
|
|
274
|
+
* `service.instance.id`, `deployment.environment.name`), which is what a
|
|
275
|
+
* collector mapping log fields onto resource attributes reads directly.
|
|
276
|
+
*
|
|
277
|
+
* Both carry the same values; only the key shape differs.
|
|
278
|
+
*/
|
|
279
|
+
resourceFormat?: 'nested' | 'flat';
|
|
280
|
+
/**
|
|
281
|
+
* Field carrying the machine-readable event name, mirroring `logKey`.
|
|
282
|
+
* Default `'event.name'`; `false` emits nothing.
|
|
283
|
+
*
|
|
284
|
+
* `logKey` stays exactly as it is — this is additive, never a rename.
|
|
285
|
+
*
|
|
286
|
+
* The value is meant to be mapped onto the **`EventName`** field of the
|
|
287
|
+
* OpenTelemetry LogRecord, which is Stable in the Logs Data Model. Note that
|
|
288
|
+
* the same-named `event.name` *attribute* is Deprecated in the semantic
|
|
289
|
+
* conventions precisely because the value belongs in that top-level field
|
|
290
|
+
* instead; the key here is the JSON carrier, not an OTLP attribute. The name is
|
|
291
|
+
* configurable so a pipeline that maps a different key can use it.
|
|
292
|
+
*
|
|
293
|
+
* Keep event names LOW cardinality — `payment.failed`, not
|
|
294
|
+
* `payment.failed.918231781`. Identifiers belong in their own fields.
|
|
295
|
+
*/
|
|
296
|
+
eventNameField?: string | false;
|
|
297
|
+
/**
|
|
298
|
+
* Shape of the error fields on an entry. Default `'pino'`.
|
|
299
|
+
*
|
|
300
|
+
* - `'pino'` — only the legacy `err` object (`type`, `message`, `stack`,
|
|
301
|
+
* plus the `cause` chain). What every existing query reads.
|
|
302
|
+
* - `'semconv'` — only the OpenTelemetry attributes: `exception.type`,
|
|
303
|
+
* `exception.message`, `exception.stacktrace` and `error.type`. The `err`
|
|
304
|
+
* object is removed, so this is an explicit migration, never a default.
|
|
305
|
+
* - `'both'` — both shapes on the same entry. Recommended while moving
|
|
306
|
+
* dashboards over: nothing breaks, and the new fields are already there.
|
|
307
|
+
*
|
|
308
|
+
* All four attributes are **Stable** in Semantic Conventions v1.44.0.
|
|
309
|
+
* `error.type` carries the error's class name and is LOW cardinality by
|
|
310
|
+
* construction — never a message, never an identifier.
|
|
311
|
+
*/
|
|
312
|
+
errorFormat?: 'pino' | 'semconv' | 'both';
|
|
179
313
|
/** Censor string written in place of redacted values. Default: `[REDACTED]`. */
|
|
180
314
|
redactCensor?: string;
|
|
181
315
|
/** Disable default redact paths (use with caution). Default: false. */
|
|
@@ -328,6 +462,127 @@ declare class BymaxLoggerModule extends BymaxLoggerModuleBase {
|
|
|
328
462
|
static useNestLogger(app: INestApplication): void;
|
|
329
463
|
}
|
|
330
464
|
|
|
465
|
+
/**
|
|
466
|
+
* Log keys reserved by `@bymax-one/nest-logger` for its own structured events.
|
|
467
|
+
*
|
|
468
|
+
* Consumer apps SHOULD NOT use these names for application-level events
|
|
469
|
+
* to avoid collision in log aggregation queries (Datadog, Loki, Elastic).
|
|
470
|
+
*
|
|
471
|
+
* Every key here is emitted by some code path, EXCEPT the ones listed in
|
|
472
|
+
* {@link RESERVED_LOG_KEYS_NOT_EMITTED}, which are reserved-only and carry the
|
|
473
|
+
* reason they stay that way. `reserved-log-keys.constants.spec.ts` enforces the
|
|
474
|
+
* split, so a key can never again be declared, documented as a signal, and then
|
|
475
|
+
* silently never written.
|
|
476
|
+
*/
|
|
477
|
+
declare const RESERVED_LOG_KEYS: Readonly<{
|
|
478
|
+
readonly LOGGER_BOOTSTRAP_OK: "LOGGER_BOOTSTRAP_OK";
|
|
479
|
+
readonly LOGGER_BOOTSTRAP_WARNING: "LOGGER_BOOTSTRAP_WARNING";
|
|
480
|
+
readonly LOGGER_SHUTDOWN_OK: "LOGGER_SHUTDOWN_OK";
|
|
481
|
+
readonly HTTP_REQUEST_START: "HTTP_REQUEST_START";
|
|
482
|
+
readonly HTTP_REQUEST_SUCCESS: "HTTP_REQUEST_SUCCESS";
|
|
483
|
+
readonly HTTP_REQUEST_REDIRECT: "HTTP_REQUEST_REDIRECT";
|
|
484
|
+
readonly HTTP_REQUEST_CLIENT_ERROR: "HTTP_REQUEST_CLIENT_ERROR";
|
|
485
|
+
readonly HTTP_REQUEST_SERVER_ERROR: "HTTP_REQUEST_SERVER_ERROR";
|
|
486
|
+
readonly HTTP_REQUEST_COMPLETED: "HTTP_REQUEST_COMPLETED";
|
|
487
|
+
readonly HTTP_REQUEST_ABORTED: "HTTP_REQUEST_ABORTED";
|
|
488
|
+
readonly HTTP_EXCEPTION_HANDLED: "HTTP_EXCEPTION_HANDLED";
|
|
489
|
+
readonly HTTP_EXCEPTION_UNHANDLED: "HTTP_EXCEPTION_UNHANDLED";
|
|
490
|
+
readonly METHOD_EXECUTION: "METHOD_EXECUTION";
|
|
491
|
+
readonly METHOD_SLOW_EXECUTION: "METHOD_SLOW_EXECUTION";
|
|
492
|
+
readonly LOGGER_DESTINATION_INIT_FAILED: "LOGGER_DESTINATION_INIT_FAILED";
|
|
493
|
+
readonly LOGGER_DESTINATION_WRITE_FAILED: "LOGGER_DESTINATION_WRITE_FAILED";
|
|
494
|
+
readonly LOGGER_ENTRY_TRUNCATED: "LOGGER_ENTRY_TRUNCATED";
|
|
495
|
+
readonly LOGGER_REDACTION_FAILED: "LOGGER_REDACTION_FAILED";
|
|
496
|
+
}>;
|
|
497
|
+
/**
|
|
498
|
+
* Keys that are reserved but intentionally never emitted, each with the reason.
|
|
499
|
+
*
|
|
500
|
+
* A name in this map is claimed so no application event can take it, but the
|
|
501
|
+
* library commits to not writing it. Anything NOT listed here must be reachable
|
|
502
|
+
* from some code path — the spec asserts both directions.
|
|
503
|
+
*/
|
|
504
|
+
declare const RESERVED_LOG_KEYS_NOT_EMITTED: Readonly<{
|
|
505
|
+
/**
|
|
506
|
+
* Superseded before it was ever used. The request lifecycle terminates in one
|
|
507
|
+
* of `HTTP_REQUEST_SUCCESS` / `_REDIRECT` / `_CLIENT_ERROR` / `_SERVER_ERROR`,
|
|
508
|
+
* each carrying the same `duration` a generic "completed" entry would. Emitting
|
|
509
|
+
* it as well would double the terminal access-log volume to say nothing new.
|
|
510
|
+
* It stays claimed so an application cannot repurpose a name that reads like a
|
|
511
|
+
* library event.
|
|
512
|
+
*/
|
|
513
|
+
readonly HTTP_REQUEST_COMPLETED: "reserved: superseded by the four status-specific terminal keys";
|
|
514
|
+
}>;
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Name-based recursive redaction.
|
|
518
|
+
*
|
|
519
|
+
* Layer: server/utils — the engine behind the library's DEFAULT PII protection.
|
|
520
|
+
* It walks a log record once and censors every value whose KEY NAME is in the
|
|
521
|
+
* sensitive set, to a bounded depth (see {@link REDACT_MAX_TRAVERSAL_DEPTH}).
|
|
522
|
+
*
|
|
523
|
+
* Why this replaced path matching. The previous default compiled 140
|
|
524
|
+
* `fast-redact` paths, 108 of them multi-level wildcards (`*.password`,
|
|
525
|
+
* `*.*.password`, …). `fast-redact` walks every key at every listed level on
|
|
526
|
+
* every log call, so the cost grew with the PATH COUNT, not with the payload:
|
|
527
|
+
* measured at ~107 µs per entry (≈9.3 k logs/s) against ~0.9 µs with redaction
|
|
528
|
+
* off. A single traversal is O(nodes) — the same order Pino's serializer already
|
|
529
|
+
* pays. The shipped configuration measures 462 k logs/s against 9.3 k, for
|
|
530
|
+
* byte-identical output on every payload the old paths covered.
|
|
531
|
+
*
|
|
532
|
+
* It is also STRICTLY SAFER than the paths it replaces:
|
|
533
|
+
* - depth 100 instead of four, and past it the value is DROPPED rather than
|
|
534
|
+
* passed through, so the ceiling cannot become a leak the way the old one
|
|
535
|
+
* was (anything below the wildcard list's fourth level was emitted in clear);
|
|
536
|
+
* - a sensitive key is caught wherever it appears, so a headers bag logged as
|
|
537
|
+
* `{ headers: { authorization } }` is covered, not only the exact
|
|
538
|
+
* `req.headers.authorization` shape the absolute paths pinned.
|
|
539
|
+
*
|
|
540
|
+
* Three value kinds need special handling, and getting any of them wrong is a
|
|
541
|
+
* leak rather than a cosmetic issue:
|
|
542
|
+
* - `Error` — copied through its prototype and descriptors, so it stays an
|
|
543
|
+
* `Error` (Pino's `err` serializer keys off the instance, and `message` /
|
|
544
|
+
* `stack` are own but NON-enumerable). Skipping errors entirely left an
|
|
545
|
+
* error under any key WITHOUT a serializer serializing its own enumerable
|
|
546
|
+
* properties in clear.
|
|
547
|
+
* - anything carrying `toJSON` (`Date`, `Decimal`, Luxon, Prisma types) — the
|
|
548
|
+
* method decides the serialized form, so the walk inspects its OUTPUT.
|
|
549
|
+
* Skipping these let a `toJSON` SYNTHESIZE a secret straight past both hooks.
|
|
550
|
+
* - `ArrayBuffer` views (`Buffer`, typed arrays) — indexed byte containers,
|
|
551
|
+
* returned untouched.
|
|
552
|
+
*
|
|
553
|
+
* What this file cannot reach: bindings passed to `child()`. Pino pre-serializes
|
|
554
|
+
* them into the instance's `chindings` fragment before any formatter runs, so
|
|
555
|
+
* `PinoLoggerService.child()` applies this redactor itself.
|
|
556
|
+
*
|
|
557
|
+
* Contract: SNAPSHOT and NEVER-THROW. Nothing the caller passed is mutated; the
|
|
558
|
+
* result is a fresh structure built from values read exactly ONCE. That is a
|
|
559
|
+
* deliberate trade against an earlier copy-on-write design which returned a
|
|
560
|
+
* clean subtree by reference: doing so left every accessor in it to be evaluated
|
|
561
|
+
* a SECOND time by `JSON.stringify`, and a stateful getter (or `toJSON`) can
|
|
562
|
+
* answer clean to the walk and `{ password }` to the serializer. Inspection
|
|
563
|
+
* cannot close that window — only reading once and pinning the result can. It
|
|
564
|
+
* costs ~26 % of the entry throughput, measured, and the walk is still ~36x
|
|
565
|
+
* faster than the path expansion it replaced.
|
|
566
|
+
*
|
|
567
|
+
* Any internal failure — including a censor that cannot be written — degrades the
|
|
568
|
+
* whole record to a marked, secret-free envelope rather than crashing the request
|
|
569
|
+
* that produced it.
|
|
570
|
+
*
|
|
571
|
+
* @see {@link createNameRedactor}
|
|
572
|
+
*/
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* A configured redactor.
|
|
576
|
+
*
|
|
577
|
+
* @param value - The value to redact.
|
|
578
|
+
* @param isRecordRoot - `true` when `value` is the ROOT of a log record — the
|
|
579
|
+
* object Pino iterates rather than serializes. A root must not honour a
|
|
580
|
+
* `toJSON` method, because doing so would replace the entire record with that
|
|
581
|
+
* method's return value; nested values and serializer outputs do reach
|
|
582
|
+
* `JSON.stringify` and keep it.
|
|
583
|
+
*/
|
|
584
|
+
type Redactor = (value: unknown, isRecordRoot?: boolean) => unknown;
|
|
585
|
+
|
|
331
586
|
/**
|
|
332
587
|
* Pino-backed implementation of the NestJS `LoggerService` contract plus the
|
|
333
588
|
* structured `MODULE_ACTION_RESULT` API.
|
|
@@ -346,8 +601,15 @@ declare class BymaxLoggerModule extends BymaxLoggerModuleBase {
|
|
|
346
601
|
*/
|
|
347
602
|
declare class PinoLoggerService implements LoggerService, OnApplicationShutdown {
|
|
348
603
|
private readonly pino;
|
|
604
|
+
private readonly redact;
|
|
349
605
|
private context?;
|
|
350
|
-
|
|
606
|
+
/**
|
|
607
|
+
* @param pino - The wrapped Pino instance.
|
|
608
|
+
* @param redact - The DEFAULT-coverage redactor, applied to `child()` bindings.
|
|
609
|
+
* Defaults to the identity so a hand-built instance (benchmarks, unit tests)
|
|
610
|
+
* still works; the module always injects the configured one.
|
|
611
|
+
*/
|
|
612
|
+
constructor(pino: Logger, redact?: Redactor);
|
|
351
613
|
/**
|
|
352
614
|
* NestJS `log` (info level). Last string parameter is treated as the context.
|
|
353
615
|
*
|
|
@@ -455,17 +717,6 @@ declare class PinoLoggerService implements LoggerService, OnApplicationShutdown
|
|
|
455
717
|
* this service has no additional teardown work.
|
|
456
718
|
*/
|
|
457
719
|
onApplicationShutdown(): void;
|
|
458
|
-
/**
|
|
459
|
-
* Serialize an Error into a plain, log-safe object matching Pino's built-in
|
|
460
|
-
* `err` serializer shape (`type`, `message`, `stack`).
|
|
461
|
-
*
|
|
462
|
-
* Reads `error.name` (NOT `error.constructor.name`) for `type`: native
|
|
463
|
-
* subclasses set `name` to their class name, and a sanitized plain-object error
|
|
464
|
-
* (from `sanitizeError`) carries the real class name on `name` — whereas its
|
|
465
|
-
* `constructor.name` would be the unhelpful `'Object'`, breaking `err.type`
|
|
466
|
-
* dashboards for every exception logged through `HttpExceptionFilter`.
|
|
467
|
-
*/
|
|
468
|
-
private serializeError;
|
|
469
720
|
/** Resolve the context: last string param wins, else the instance context. */
|
|
470
721
|
private resolveContext;
|
|
471
722
|
/** Emit a structured payload at info or warn level. */
|
|
@@ -701,6 +952,21 @@ declare class HttpLoggingInterceptor implements NestInterceptor {
|
|
|
701
952
|
* @returns The (unmodified) response stream.
|
|
702
953
|
*/
|
|
703
954
|
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
|
|
955
|
+
/**
|
|
956
|
+
* Hand the lifecycle to the access-log middleware, recording only the thrown
|
|
957
|
+
* error for its terminal entry.
|
|
958
|
+
*
|
|
959
|
+
* The interceptor cannot be the recorder: it never runs for a request a guard
|
|
960
|
+
* rejected or one that matched no route. It also cannot judge DELIVERY — at the
|
|
961
|
+
* moment it completes, the response has not been flushed yet, which is why an
|
|
962
|
+
* aborted request used to be logged as the 200 the handler intended. What it
|
|
963
|
+
* uniquely has is the thrown value, so that is all it contributes.
|
|
964
|
+
*
|
|
965
|
+
* @param req - The request whose lifecycle the middleware owns.
|
|
966
|
+
* @param next - The downstream call handler.
|
|
967
|
+
* @returns The response stream, unmodified and with exceptions re-thrown.
|
|
968
|
+
*/
|
|
969
|
+
private recordOnly;
|
|
704
970
|
/**
|
|
705
971
|
* Log a non-throwing completion: SUCCESS for 2xx, REDIRECT for 3xx, nothing
|
|
706
972
|
* for any other status (those are surfaced via the error path instead).
|
|
@@ -731,8 +997,12 @@ declare class HttpLoggingInterceptor implements NestInterceptor {
|
|
|
731
997
|
*
|
|
732
998
|
* Layer: server/middlewares — NestJS requires middleware to be wired in a
|
|
733
999
|
* module's `configure(consumer)` hook, which the library cannot do on the
|
|
734
|
-
* consumer's behalf. This helper encapsulates the
|
|
735
|
-
* import the middleware
|
|
1000
|
+
* consumer's behalf. This helper encapsulates the wiring so consumers do not
|
|
1001
|
+
* import the middleware classes directly.
|
|
1002
|
+
*
|
|
1003
|
+
* It registers TWO middlewares, in order: the correlation scope, then the access
|
|
1004
|
+
* log that runs inside it. Both run before any guard, which is the whole point —
|
|
1005
|
+
* an interceptor never observes a request a guard rejected.
|
|
736
1006
|
*/
|
|
737
1007
|
|
|
738
1008
|
/**
|
|
@@ -791,6 +1061,18 @@ interface LoggableRequest {
|
|
|
791
1061
|
readonly method: string;
|
|
792
1062
|
/** Request target, normalized and query-stripped before it reaches a log. */
|
|
793
1063
|
readonly url: string;
|
|
1064
|
+
/**
|
|
1065
|
+
* The request target as it arrived, before any mount point was stripped.
|
|
1066
|
+
*
|
|
1067
|
+
* Middleware mounted at a path sees `url` RELATIVE to that mount: measured
|
|
1068
|
+
* under `setGlobalPrefix('api')`, a request for `/api/users/7` reaches a
|
|
1069
|
+
* root-mounted middleware as `url = '/users/7'` and
|
|
1070
|
+
* `originalUrl = '/api/users/7'`, and `/api` itself arrives as `url = '/'`.
|
|
1071
|
+
* Logging `url` there would drop the prefix from every entry and make an
|
|
1072
|
+
* `excludePaths` pattern written against the real path stop matching. Absent
|
|
1073
|
+
* on adapters that do not provide it, in which case `url` is already whole.
|
|
1074
|
+
*/
|
|
1075
|
+
readonly originalUrl?: string | undefined;
|
|
794
1076
|
/** Client address when the adapter resolves one. */
|
|
795
1077
|
readonly ip?: string | undefined;
|
|
796
1078
|
/**
|
|
@@ -811,12 +1093,26 @@ interface LoggableRequest {
|
|
|
811
1093
|
interface LoggableResponse {
|
|
812
1094
|
/** Status code, read when logging the completed request. */
|
|
813
1095
|
readonly statusCode: number;
|
|
1096
|
+
/**
|
|
1097
|
+
* `true` once every byte has been flushed to the socket. Read in the `'close'`
|
|
1098
|
+
* handler to tell a delivered response from one the client never received:
|
|
1099
|
+
* `'close'` fires for both, and `'finish'` never fires for an aborted request
|
|
1100
|
+
* at all. It is what keeps an aborted request from being logged as the success
|
|
1101
|
+
* the handler intended.
|
|
1102
|
+
*/
|
|
1103
|
+
readonly writableFinished: boolean;
|
|
814
1104
|
/** Sets a response header (used for the correlation id). */
|
|
815
1105
|
setHeader(name: string, value: string): void;
|
|
816
1106
|
/** Selects the status code and returns the chainable body writer. */
|
|
817
1107
|
status(code: number): {
|
|
818
1108
|
json(body: unknown): unknown;
|
|
819
1109
|
};
|
|
1110
|
+
/**
|
|
1111
|
+
* Registers a lifecycle listener. Only `'close'` is used: it fires exactly
|
|
1112
|
+
* once whether the response completed or the connection died early, where
|
|
1113
|
+
* `'finish'` is blind to the aborted case.
|
|
1114
|
+
*/
|
|
1115
|
+
on(event: 'close', listener: () => void): unknown;
|
|
820
1116
|
}
|
|
821
1117
|
/** Continuation passed to a middleware; invoked with no arguments here. */
|
|
822
1118
|
type NextHandler = () => void;
|
|
@@ -928,9 +1224,12 @@ declare const LOGGER_DESTINATIONS_TOKEN: unique symbol;
|
|
|
928
1224
|
declare const LOG_CONTEXT_TOKEN: unique symbol;
|
|
929
1225
|
|
|
930
1226
|
/**
|
|
931
|
-
*
|
|
932
|
-
*
|
|
933
|
-
* absolute
|
|
1227
|
+
* The legacy `fast-redact` path expansion of {@link REDACT_COMMON_FIELDS}:
|
|
1228
|
+
* every name at the record root plus wildcard depths 1–4, followed by the
|
|
1229
|
+
* absolute header paths. Length is derived, not fixed — `fields × 5 + absolute`.
|
|
1230
|
+
*
|
|
1231
|
+
* Fed to Pino only under the opt-in `redactStrategy: 'paths'`. Kept exported at
|
|
1232
|
+
* full fidelity because it is public API and append-only by contract.
|
|
934
1233
|
*
|
|
935
1234
|
* The bare field names come first, at depth 0 — the log record's own root.
|
|
936
1235
|
* `emitStructured` spreads caller metadata there (`{ ...metadata, logKey, ... }`),
|
|
@@ -942,24 +1241,60 @@ declare const LOG_CONTEXT_TOKEN: unique symbol;
|
|
|
942
1241
|
*/
|
|
943
1242
|
declare const DEFAULT_REDACT_PATHS: readonly string[];
|
|
944
1243
|
|
|
1244
|
+
/**
|
|
1245
|
+
* Pino numeric level mapping. Frozen at runtime so destinations and the
|
|
1246
|
+
* NestJS bridge can rely on a stable wire-format contract.
|
|
1247
|
+
*
|
|
1248
|
+
* @see {@link https://github.com/pinojs/pino/blob/main/docs/api.md#level-string}
|
|
1249
|
+
*/
|
|
1250
|
+
declare const PINO_LEVEL_NUMBERS: Record<LogLevel, number>;
|
|
1251
|
+
/**
|
|
1252
|
+
* Reverse lookup — numeric Pino level → string. Derived from
|
|
1253
|
+
* {@link PINO_LEVEL_NUMBERS} so the two cannot drift, then frozen so it is
|
|
1254
|
+
* read-only at runtime.
|
|
1255
|
+
*/
|
|
1256
|
+
declare const PINO_LEVEL_NAMES: Record<number, LogLevel>;
|
|
1257
|
+
|
|
945
1258
|
/**
|
|
946
1259
|
* Shape of a single log entry as serialized to JSON output.
|
|
947
1260
|
*
|
|
948
|
-
* Used by destinations and downstream parsers (
|
|
1261
|
+
* Used by destinations and downstream parsers (Loki, Elastic, Datadog).
|
|
949
1262
|
* The trailing index signature `[key: string]: unknown` covers arbitrary
|
|
950
1263
|
* structured fields injected by application logs.
|
|
1264
|
+
*
|
|
1265
|
+
* `level` and `time` describe what the library ACTUALLY writes. Before `1.2.0`
|
|
1266
|
+
* they were declared `number`, which the runtime never produced: the Pino
|
|
1267
|
+
* instance is built with `formatters.level` returning the string label and a
|
|
1268
|
+
* `timestamp` function emitting an ISO 8601 string. A destination written
|
|
1269
|
+
* against the old declaration type-checked and then failed at runtime — the
|
|
1270
|
+
* README's own Loki example called `BigInt(entry.time)` on an ISO string, which
|
|
1271
|
+
* throws. See `log-entry-contract.e2e-spec.ts`, which asserts the declaration
|
|
1272
|
+
* against a real serialized record so the two can never drift again.
|
|
951
1273
|
*/
|
|
952
1274
|
interface LogEntry {
|
|
953
|
-
/**
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
1275
|
+
/**
|
|
1276
|
+
* Severity as the Pino string label (`'info'`, `'error'`, …) — NOT the numeric
|
|
1277
|
+
* code. Convert with `PINO_LEVEL_NUMBERS` when a numeric column is needed.
|
|
1278
|
+
*/
|
|
1279
|
+
level: LogLevel;
|
|
1280
|
+
/**
|
|
1281
|
+
* ISO 8601 UTC timestamp string, e.g. `'2026-08-12T09:25:46.520Z'`. Parse with
|
|
1282
|
+
* `Date.parse(entry.time)` for epoch milliseconds. A consumer-supplied
|
|
1283
|
+
* `timestamp` option may change the format, but never the type.
|
|
1284
|
+
*/
|
|
1285
|
+
time: string;
|
|
957
1286
|
/** Human-readable message. */
|
|
958
1287
|
msg: string;
|
|
959
1288
|
/** Convention key — `MODULE_ACTION_RESULT` format. */
|
|
960
1289
|
logKey?: string;
|
|
961
|
-
/**
|
|
962
|
-
|
|
1290
|
+
/**
|
|
1291
|
+
* Service identity as EMITTED — see {@link EmittedServiceResource}. Not the
|
|
1292
|
+
* configuration shape: `service.instance.id` nests, and the environment lives
|
|
1293
|
+
* under `deployment`, not here.
|
|
1294
|
+
*/
|
|
1295
|
+
service?: EmittedServiceResource;
|
|
1296
|
+
/** Deployment identity as emitted — see {@link EmittedDeploymentResource}. */
|
|
1297
|
+
deployment?: EmittedDeploymentResource;
|
|
963
1298
|
/** Optional NestJS context (typically the emitting class name). */
|
|
964
1299
|
context?: string;
|
|
965
1300
|
/** Optional correlation IDs propagated via AsyncLocalStorage. */
|
|
@@ -1002,29 +1337,4 @@ interface LogEntry {
|
|
|
1002
1337
|
*/
|
|
1003
1338
|
declare const LOG_KEYS_CONVENTION_REGEX: RegExp;
|
|
1004
1339
|
|
|
1005
|
-
|
|
1006
|
-
* Log keys reserved by `@bymax-one/nest-logger` for its own structured events.
|
|
1007
|
-
*
|
|
1008
|
-
* Consumer apps SHOULD NOT use these names for application-level events
|
|
1009
|
-
* to avoid collision in log aggregation queries (Datadog, Loki, Elastic).
|
|
1010
|
-
*/
|
|
1011
|
-
declare const RESERVED_LOG_KEYS: Readonly<{
|
|
1012
|
-
readonly LOGGER_BOOTSTRAP_OK: "LOGGER_BOOTSTRAP_OK";
|
|
1013
|
-
readonly LOGGER_BOOTSTRAP_WARNING: "LOGGER_BOOTSTRAP_WARNING";
|
|
1014
|
-
readonly LOGGER_SHUTDOWN_OK: "LOGGER_SHUTDOWN_OK";
|
|
1015
|
-
readonly HTTP_REQUEST_START: "HTTP_REQUEST_START";
|
|
1016
|
-
readonly HTTP_REQUEST_SUCCESS: "HTTP_REQUEST_SUCCESS";
|
|
1017
|
-
readonly HTTP_REQUEST_REDIRECT: "HTTP_REQUEST_REDIRECT";
|
|
1018
|
-
readonly HTTP_REQUEST_CLIENT_ERROR: "HTTP_REQUEST_CLIENT_ERROR";
|
|
1019
|
-
readonly HTTP_REQUEST_SERVER_ERROR: "HTTP_REQUEST_SERVER_ERROR";
|
|
1020
|
-
readonly HTTP_REQUEST_COMPLETED: "HTTP_REQUEST_COMPLETED";
|
|
1021
|
-
readonly HTTP_EXCEPTION_HANDLED: "HTTP_EXCEPTION_HANDLED";
|
|
1022
|
-
readonly HTTP_EXCEPTION_UNHANDLED: "HTTP_EXCEPTION_UNHANDLED";
|
|
1023
|
-
readonly METHOD_EXECUTION: "METHOD_EXECUTION";
|
|
1024
|
-
readonly METHOD_SLOW_EXECUTION: "METHOD_SLOW_EXECUTION";
|
|
1025
|
-
readonly LOGGER_DESTINATION_INIT_FAILED: "LOGGER_DESTINATION_INIT_FAILED";
|
|
1026
|
-
readonly LOGGER_DESTINATION_WRITE_FAILED: "LOGGER_DESTINATION_WRITE_FAILED";
|
|
1027
|
-
readonly LOGGER_ENTRY_TRUNCATED: "LOGGER_ENTRY_TRUNCATED";
|
|
1028
|
-
}>;
|
|
1029
|
-
|
|
1030
|
-
export { BymaxLoggerModule, type BymaxLoggerModuleAsyncOptions, type BymaxLoggerModuleOptions, type BymaxLoggerModuleOptionsFactory, DEFAULT_REDACT_PATHS, DefaultStdoutDestination, HttpExceptionFilter, HttpLoggingInterceptor, type HttpOptions, type ILogDestination, type IncomingHeaders, InjectLogger, LOGGER_DESTINATIONS_TOKEN, LOGGER_OPTIONS_TOKEN, LOGGER_PINO_INSTANCE_TOKEN, LOG_CONTEXT_METADATA_KEY, LOG_CONTEXT_TOKEN, LOG_KEYS_CONVENTION_REGEX, LogContext, type LogContext$1 as LogContextBag, LogContextService, type LogEntry, type LogLevel, LogPerformance, type LoggableRequest, type LoggableResponse, type NextHandler, type OtelOptions, PinoLoggerService, PrettyDevDestination, RESERVED_LOG_KEYS, RequestIdMiddleware, type ResolvedBymaxLoggerModuleOptions, type ServiceMetadata, applyRequestIdMiddleware };
|
|
1340
|
+
export { BymaxLoggerModule, type BymaxLoggerModuleAsyncOptions, type BymaxLoggerModuleOptions, type BymaxLoggerModuleOptionsFactory, DEFAULT_REDACT_PATHS, DefaultStdoutDestination, HttpExceptionFilter, HttpLoggingInterceptor, type HttpOptions, type ILogDestination, type IncomingHeaders, InjectLogger, LOGGER_DESTINATIONS_TOKEN, LOGGER_OPTIONS_TOKEN, LOGGER_PINO_INSTANCE_TOKEN, LOG_CONTEXT_METADATA_KEY, LOG_CONTEXT_TOKEN, LOG_KEYS_CONVENTION_REGEX, LogContext, type LogContext$1 as LogContextBag, LogContextService, type LogEntry, type LogLevel, LogPerformance, type LoggableRequest, type LoggableResponse, type NextHandler, type OtelOptions, PINO_LEVEL_NAMES, PINO_LEVEL_NUMBERS, PinoLoggerService, PrettyDevDestination, RESERVED_LOG_KEYS, RESERVED_LOG_KEYS_NOT_EMITTED, RequestIdMiddleware, type ResolvedBymaxLoggerModuleOptions, type ServiceMetadata, applyRequestIdMiddleware };
|