@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.
@@ -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
- /** Additional Pino redact paths — merged with `DEFAULT_REDACT_PATHS`. */
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
- constructor(pino: Logger);
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 one-liner so consumers do not
735
- * import the middleware class directly.
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
- * Canonical list of redact paths merged into Pino's `redact.paths` option by
932
- * default. Expected length: 27 root fields + 27 common fields × 4 depths + 5
933
- * absolute = **140**.
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 (Datadog, Loki, Elastic).
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
- /** Pino numeric level (30 = info, 50 = error, etc.). */
954
- level: number;
955
- /** ISO 8601 UTC timestamp string OR epoch milliseconds (numeric). */
956
- time: string | number;
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
- /** Service metadata snapshot — see {@link ServiceMetadata}. */
962
- service?: ServiceMetadata;
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 };