@bymax-one/nest-logger 1.2.1 → 1.2.2

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 CHANGED
@@ -11,6 +11,27 @@ heading here.
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [1.2.2] - 2026-08-13
15
+
16
+ ### Added
17
+
18
+ - **`EmittedServiceResource` and `EmittedDeploymentResource` are exported.** `LogEntry['service']`
19
+ and `LogEntry['deployment']` have been typed by these interfaces since 1.2.1, but neither was
20
+ reexported from `@bymax-one/nest-logger/shared`, so a consumer could hold `entry.service` and had
21
+ no way to write its type — a function taking one had to inline the shape or reach for
22
+ `LogEntry['service']`. Reported by a consumer adopting 1.2.1.
23
+ - **Every shared type is now reexported from the server subpath, not a subset.** `LogEntry` was
24
+ already there while the types of its own properties were not, which is the same defect one level
25
+ up. `ResolvedServiceMetadata` and `ReservedLogKey` join them for the same reason.
26
+ - **`LogEntry` declares `'event.name'`.** The field has been emitted since 1.2.1 and documented as
27
+ part of the entry, but only the index signature covered it — so a consumer reading
28
+ `entry['event.name']` got `unknown` and needed a cast. A documented field that cannot be read
29
+ without a cast is not really published. The declaration describes the default key; a renamed
30
+ `eventNameField` still arrives through the index signature.
31
+
32
+ Type-only change: `dist/*.mjs` and `dist/*.cjs` are byte-identical to 1.2.1, since `export type`
33
+ erases at compile time. Only the `.d.ts` files differ.
34
+
14
35
  ## [1.2.1] - 2026-08-13
15
36
 
16
37
  ### Security
@@ -793,7 +814,8 @@ published `dist/` is identical — no runtime behaviour changes for consumers.
793
814
  - Professional CI suite: `ci.yml`, `bench.yml`, `codeql.yml`, `scorecard.yml`,
794
815
  `release.yml`, Dependabot, and issue templates
795
816
 
796
- [Unreleased]: https://github.com/bymaxone/nest-logger/compare/v1.2.1...HEAD
817
+ [Unreleased]: https://github.com/bymaxone/nest-logger/compare/v1.2.2...HEAD
818
+ [1.2.2]: https://github.com/bymaxone/nest-logger/compare/v1.2.1...v1.2.2
797
819
  [1.2.1]: https://github.com/bymaxone/nest-logger/compare/v1.2.0...v1.2.1
798
820
  [1.2.0]: https://github.com/bymaxone/nest-logger/compare/v1.1.0...v1.2.0
799
821
  [1.1.0]: https://github.com/bymaxone/nest-logger/compare/v1.0.8...v1.1.0
package/README.md CHANGED
@@ -101,10 +101,10 @@ pnpm add @bymax-one/nest-logger
101
101
 
102
102
  One package, two entry points — import only what your app needs:
103
103
 
104
- | Subpath | Import | Purpose | Dependencies |
105
- | ---------- | ------------------------------- | ------------------------------------------------------------------------------- | :-------------------------------------: |
106
- | **Server** | `@bymax-one/nest-logger` | NestJS module, logger service, interceptor, filter, decorators, destinations | NestJS 11, pino, rxjs, reflect-metadata |
107
- | **Shared** | `@bymax-one/nest-logger/shared` | Types, constants, the log-key regex — `LogLevel`, `LogEntry`, `ServiceMetadata` | None |
104
+ | Subpath | Import | Purpose | Dependencies |
105
+ | ---------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------: |
106
+ | **Server** | `@bymax-one/nest-logger` | NestJS module, logger service, interceptor, filter, decorators, destinations | NestJS 11, pino, rxjs, reflect-metadata |
107
+ | **Shared** | `@bymax-one/nest-logger/shared` | Types, constants, the log-key regex — `LogLevel`, `LogEntry`, `ServiceMetadata`, `ResolvedServiceMetadata`, `EmittedServiceResource`, `EmittedDeploymentResource` | None |
108
108
 
109
109
  ```
110
110
  shared (zero deps)
@@ -1093,7 +1093,28 @@ PINO_LEVEL_NAMES[30] // 'info' — numeric code → label
1093
1093
  ```typescript
1094
1094
  type LogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'
1095
1095
 
1096
- type ServiceMetadata = { name: string; version: string }
1096
+ // What you CONFIGURE.
1097
+ type ServiceMetadata = {
1098
+ readonly name: string
1099
+ readonly version: string
1100
+ readonly namespace?: string
1101
+ readonly instanceId?: string
1102
+ readonly environment?: string
1103
+ }
1104
+
1105
+ // What is EMITTED. Deliberately a different type: `instanceId` nests as
1106
+ // `service.instance.id`, and the environment is not under `service` at all.
1107
+ // Reusing the configuration type here would advertise `entry.service.instanceId`,
1108
+ // which no entry carries.
1109
+ type EmittedServiceResource = {
1110
+ readonly name: string
1111
+ readonly version: string
1112
+ readonly namespace?: string
1113
+ readonly instance?: { readonly id: string }
1114
+ readonly [key: string]: unknown
1115
+ }
1116
+
1117
+ type EmittedDeploymentResource = { readonly environment: { readonly name: string } }
1097
1118
 
1098
1119
  type LogEntry = {
1099
1120
  /** Pino string label — `'info'`, not `30`. Convert with `PINO_LEVEL_NUMBERS`. */
@@ -1102,7 +1123,12 @@ type LogEntry = {
1102
1123
  time: string
1103
1124
  msg: string
1104
1125
  logKey?: string
1105
- service?: ServiceMetadata
1126
+ /** Present under `resourceFormat: 'nested'` (the default). */
1127
+ service?: EmittedServiceResource
1128
+ /** Present when an environment resolved — `deployment.environment.name`. */
1129
+ deployment?: EmittedDeploymentResource
1130
+ /** Derived from `logKey` unless `eventNameField: false`. */
1131
+ 'event.name'?: string
1106
1132
  context?: string
1107
1133
  requestId?: string
1108
1134
  tenantId?: string
@@ -1113,6 +1139,41 @@ type LogEntry = {
1113
1139
  }
1114
1140
  ```
1115
1141
 
1142
+ Under `resourceFormat: 'flat'` there is no `service` object at all — the values arrive as dotted
1143
+ top-level keys (`"service.instance.id"`), which the index signature already admits.
1144
+
1145
+ The emitted types are exported, so a helper that receives one can be typed without inlining its
1146
+ shape:
1147
+
1148
+ ```typescript
1149
+ import type {
1150
+ EmittedDeploymentResource,
1151
+ EmittedServiceResource,
1152
+ LogEntry
1153
+ } from '@bymax-one/nest-logger/shared'
1154
+
1155
+ /** Build the identity triplet a backend joins on, from a parsed entry. */
1156
+ function identityOf(entry: LogEntry): string {
1157
+ const service: EmittedServiceResource | undefined = entry.service
1158
+ const deployment: EmittedDeploymentResource | undefined = entry.deployment
1159
+ if (service === undefined) {
1160
+ return 'unknown'
1161
+ }
1162
+ // `service.namespace` is part of the identity, not decoration: OTel allows two
1163
+ // services to share a name when their namespaces differ, so a key built without
1164
+ // it collapses them into one.
1165
+ const namespace = service.namespace ?? 'no-namespace'
1166
+ const instance = service.instance?.id ?? 'no-instance'
1167
+ const environment = deployment?.environment.name ?? 'no-environment'
1168
+ return `${namespace}/${service.name}@${service.version}/${instance}/${environment}`
1169
+ }
1170
+
1171
+ /** `event.name` is declared, so it reads as a string without a cast. */
1172
+ function eventOf(entry: LogEntry): string | undefined {
1173
+ return entry['event.name']
1174
+ }
1175
+ ```
1176
+
1116
1177
  ### `ILogDestination`
1117
1178
 
1118
1179
  ```typescript
@@ -125,6 +125,26 @@ interface ServiceMetadata {
125
125
  */
126
126
  readonly environment?: string;
127
127
  }
128
+ /**
129
+ * Service identity after precedence has been applied.
130
+ *
131
+ * Distinct from {@link ServiceMetadata} because the optional fields here mean
132
+ * "resolved to nothing" rather than "not configured": every source — options,
133
+ * `OTEL_SERVICE_NAME`, `OTEL_RESOURCE_ATTRIBUTES`, `NODE_ENV` — has already been
134
+ * consulted, and a field still absent will not appear on any entry.
135
+ */
136
+ interface ResolvedServiceMetadata {
137
+ /** `service.name`, always present. */
138
+ name: string;
139
+ /** `service.version`, always present. */
140
+ version: string;
141
+ /** `service.namespace`, when any source supplied one. */
142
+ namespace?: string;
143
+ /** `service.instance.id`, when any source supplied one. */
144
+ instanceId?: string;
145
+ /** `deployment.environment.name`, when any source supplied one. */
146
+ environment?: string;
147
+ }
128
148
  /**
129
149
  * The `service` block as it appears in an EMITTED record, under the default
130
150
  * `resourceFormat: 'nested'`.
@@ -512,6 +532,11 @@ declare const RESERVED_LOG_KEYS_NOT_EMITTED: Readonly<{
512
532
  */
513
533
  readonly HTTP_REQUEST_COMPLETED: "reserved: superseded by the four status-specific terminal keys";
514
534
  }>;
535
+ /**
536
+ * Strongly-typed union of every reserved log key value, derived from
537
+ * {@link RESERVED_LOG_KEYS}. Use this type to constrain log-key arguments.
538
+ */
539
+ type ReservedLogKey = (typeof RESERVED_LOG_KEYS)[keyof typeof RESERVED_LOG_KEYS];
515
540
 
516
541
  /**
517
542
  * Name-based recursive redaction.
@@ -1287,6 +1312,17 @@ interface LogEntry {
1287
1312
  msg: string;
1288
1313
  /** Convention key — `MODULE_ACTION_RESULT` format. */
1289
1314
  logKey?: string;
1315
+ /**
1316
+ * OTel event name derived from {@link logKey} (`PAYMENT_FAILED` →
1317
+ * `payment.failed`), emitted on structured calls unless `eventNameField` is
1318
+ * `false`. Declared explicitly because the index signature below would type
1319
+ * it `unknown`: a documented field a consumer cannot read without a cast is
1320
+ * not really published.
1321
+ *
1322
+ * The key is configurable, so a renamed field arrives through the index
1323
+ * signature instead — this declaration describes the default only.
1324
+ */
1325
+ 'event.name'?: string;
1290
1326
  /**
1291
1327
  * Service identity as EMITTED — see {@link EmittedServiceResource}. Not the
1292
1328
  * configuration shape: `service.instance.id` nests, and the environment lives
@@ -1337,4 +1373,4 @@ interface LogEntry {
1337
1373
  */
1338
1374
  declare const LOG_KEYS_CONVENTION_REGEX: RegExp;
1339
1375
 
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 };
1376
+ export { BymaxLoggerModule, type BymaxLoggerModuleAsyncOptions, type BymaxLoggerModuleOptions, type BymaxLoggerModuleOptionsFactory, DEFAULT_REDACT_PATHS, DefaultStdoutDestination, type EmittedDeploymentResource, type EmittedServiceResource, 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 ReservedLogKey, type ResolvedBymaxLoggerModuleOptions, type ResolvedServiceMetadata, type ServiceMetadata, applyRequestIdMiddleware };
@@ -125,6 +125,26 @@ interface ServiceMetadata {
125
125
  */
126
126
  readonly environment?: string;
127
127
  }
128
+ /**
129
+ * Service identity after precedence has been applied.
130
+ *
131
+ * Distinct from {@link ServiceMetadata} because the optional fields here mean
132
+ * "resolved to nothing" rather than "not configured": every source — options,
133
+ * `OTEL_SERVICE_NAME`, `OTEL_RESOURCE_ATTRIBUTES`, `NODE_ENV` — has already been
134
+ * consulted, and a field still absent will not appear on any entry.
135
+ */
136
+ interface ResolvedServiceMetadata {
137
+ /** `service.name`, always present. */
138
+ name: string;
139
+ /** `service.version`, always present. */
140
+ version: string;
141
+ /** `service.namespace`, when any source supplied one. */
142
+ namespace?: string;
143
+ /** `service.instance.id`, when any source supplied one. */
144
+ instanceId?: string;
145
+ /** `deployment.environment.name`, when any source supplied one. */
146
+ environment?: string;
147
+ }
128
148
  /**
129
149
  * The `service` block as it appears in an EMITTED record, under the default
130
150
  * `resourceFormat: 'nested'`.
@@ -512,6 +532,11 @@ declare const RESERVED_LOG_KEYS_NOT_EMITTED: Readonly<{
512
532
  */
513
533
  readonly HTTP_REQUEST_COMPLETED: "reserved: superseded by the four status-specific terminal keys";
514
534
  }>;
535
+ /**
536
+ * Strongly-typed union of every reserved log key value, derived from
537
+ * {@link RESERVED_LOG_KEYS}. Use this type to constrain log-key arguments.
538
+ */
539
+ type ReservedLogKey = (typeof RESERVED_LOG_KEYS)[keyof typeof RESERVED_LOG_KEYS];
515
540
 
516
541
  /**
517
542
  * Name-based recursive redaction.
@@ -1287,6 +1312,17 @@ interface LogEntry {
1287
1312
  msg: string;
1288
1313
  /** Convention key — `MODULE_ACTION_RESULT` format. */
1289
1314
  logKey?: string;
1315
+ /**
1316
+ * OTel event name derived from {@link logKey} (`PAYMENT_FAILED` →
1317
+ * `payment.failed`), emitted on structured calls unless `eventNameField` is
1318
+ * `false`. Declared explicitly because the index signature below would type
1319
+ * it `unknown`: a documented field a consumer cannot read without a cast is
1320
+ * not really published.
1321
+ *
1322
+ * The key is configurable, so a renamed field arrives through the index
1323
+ * signature instead — this declaration describes the default only.
1324
+ */
1325
+ 'event.name'?: string;
1290
1326
  /**
1291
1327
  * Service identity as EMITTED — see {@link EmittedServiceResource}. Not the
1292
1328
  * configuration shape: `service.instance.id` nests, and the environment lives
@@ -1337,4 +1373,4 @@ interface LogEntry {
1337
1373
  */
1338
1374
  declare const LOG_KEYS_CONVENTION_REGEX: RegExp;
1339
1375
 
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 };
1376
+ export { BymaxLoggerModule, type BymaxLoggerModuleAsyncOptions, type BymaxLoggerModuleOptions, type BymaxLoggerModuleOptionsFactory, DEFAULT_REDACT_PATHS, DefaultStdoutDestination, type EmittedDeploymentResource, type EmittedServiceResource, 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 ReservedLogKey, type ResolvedBymaxLoggerModuleOptions, type ResolvedServiceMetadata, type ServiceMetadata, applyRequestIdMiddleware };
@@ -152,6 +152,17 @@ interface LogEntry {
152
152
  msg: string;
153
153
  /** Convention key — `MODULE_ACTION_RESULT` format. */
154
154
  logKey?: string;
155
+ /**
156
+ * OTel event name derived from {@link logKey} (`PAYMENT_FAILED` →
157
+ * `payment.failed`), emitted on structured calls unless `eventNameField` is
158
+ * `false`. Declared explicitly because the index signature below would type
159
+ * it `unknown`: a documented field a consumer cannot read without a cast is
160
+ * not really published.
161
+ *
162
+ * The key is configurable, so a renamed field arrives through the index
163
+ * signature instead — this declaration describes the default only.
164
+ */
165
+ 'event.name'?: string;
155
166
  /**
156
167
  * Service identity as EMITTED — see {@link EmittedServiceResource}. Not the
157
168
  * configuration shape: `service.instance.id` nests, and the environment lives
@@ -258,4 +269,4 @@ declare const RESERVED_LOG_KEYS_NOT_EMITTED: Readonly<{
258
269
  */
259
270
  type ReservedLogKey = (typeof RESERVED_LOG_KEYS)[keyof typeof RESERVED_LOG_KEYS];
260
271
 
261
- export { LOG_KEYS_CONVENTION_REGEX, type LogEntry, type LogLevel, RESERVED_LOG_KEYS, RESERVED_LOG_KEYS_NOT_EMITTED, type ReservedLogKey, type ResolvedServiceMetadata, type ServiceMetadata };
272
+ export { type EmittedDeploymentResource, type EmittedServiceResource, LOG_KEYS_CONVENTION_REGEX, type LogEntry, type LogLevel, RESERVED_LOG_KEYS, RESERVED_LOG_KEYS_NOT_EMITTED, type ReservedLogKey, type ResolvedServiceMetadata, type ServiceMetadata };
@@ -152,6 +152,17 @@ interface LogEntry {
152
152
  msg: string;
153
153
  /** Convention key — `MODULE_ACTION_RESULT` format. */
154
154
  logKey?: string;
155
+ /**
156
+ * OTel event name derived from {@link logKey} (`PAYMENT_FAILED` →
157
+ * `payment.failed`), emitted on structured calls unless `eventNameField` is
158
+ * `false`. Declared explicitly because the index signature below would type
159
+ * it `unknown`: a documented field a consumer cannot read without a cast is
160
+ * not really published.
161
+ *
162
+ * The key is configurable, so a renamed field arrives through the index
163
+ * signature instead — this declaration describes the default only.
164
+ */
165
+ 'event.name'?: string;
155
166
  /**
156
167
  * Service identity as EMITTED — see {@link EmittedServiceResource}. Not the
157
168
  * configuration shape: `service.instance.id` nests, and the environment lives
@@ -258,4 +269,4 @@ declare const RESERVED_LOG_KEYS_NOT_EMITTED: Readonly<{
258
269
  */
259
270
  type ReservedLogKey = (typeof RESERVED_LOG_KEYS)[keyof typeof RESERVED_LOG_KEYS];
260
271
 
261
- export { LOG_KEYS_CONVENTION_REGEX, type LogEntry, type LogLevel, RESERVED_LOG_KEYS, RESERVED_LOG_KEYS_NOT_EMITTED, type ReservedLogKey, type ResolvedServiceMetadata, type ServiceMetadata };
272
+ export { type EmittedDeploymentResource, type EmittedServiceResource, LOG_KEYS_CONVENTION_REGEX, type LogEntry, type LogLevel, RESERVED_LOG_KEYS, RESERVED_LOG_KEYS_NOT_EMITTED, type ReservedLogKey, type ResolvedServiceMetadata, type ServiceMetadata };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bymax-one/nest-logger",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "Structured JSON logging for NestJS based on Pino 10, with optional OpenTelemetry correlation.",
5
5
  "author": "Bymax One <support@bymax.one>",
6
6
  "license": "MIT",