@bymax-one/nest-logger 1.0.3 → 1.0.5
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 +107 -1
- package/README.md +29 -14
- package/dist/server/index.cjs +2 -2
- package/dist/server/index.d.cts +64 -5
- package/dist/server/index.d.ts +64 -5
- package/dist/server/index.mjs +2 -2
- package/package.json +16 -19
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,110 @@ heading here.
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [1.0.5] - 2026-07-30
|
|
15
|
+
|
|
16
|
+
### Security
|
|
17
|
+
|
|
18
|
+
- **Peer floors raised to exclude known-vulnerable NestJS versions.** `@nestjs/common ^11.0.0`
|
|
19
|
+
admitted 11.0.0–11.0.15, carrying [GHSA-cj7v-w2c7-cp7c](https://github.com/advisories/GHSA-cj7v-w2c7-cp7c)
|
|
20
|
+
(remote code execution via the `Content-Type` header, patched in 11.0.16), and
|
|
21
|
+
`@nestjs/core ^11.0.0` admitted everything up to 11.1.17, carrying
|
|
22
|
+
[GHSA-36xv-jgw5-4q75](https://github.com/advisories/GHSA-36xv-jgw5-4q75) (patched in 11.1.18).
|
|
23
|
+
|
|
24
|
+
A peer range states which versions this library supports. A floor below a published
|
|
25
|
+
advisory told a consumer a vulnerable install was supported, and nothing in their
|
|
26
|
+
tooling contradicted it — the install resolved cleanly and silently. Floors are now
|
|
27
|
+
`^11.0.16` and `^11.1.18`.
|
|
28
|
+
|
|
29
|
+
Shipped as a patch, which is where a security fix belongs; a minor would reach the
|
|
30
|
+
same installs anyway, since `^1.0.4` accepts `1.1.0` as readily as `1.0.5`. No
|
|
31
|
+
runtime behaviour changed.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- **The custom-destination examples did not compile against `ILogDestination`.**
|
|
36
|
+
The published interface takes a serialized line:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
write(payload: string): void | Promise<void> // newline-terminated JSON, UTF-8
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
All three examples in the README — `LokiDestination`, `PrismaLogDestination`,
|
|
43
|
+
`RollingFileDestination` — and the API reference table declared
|
|
44
|
+
`write(entry: LogEntry): void`, so anyone following the README to build a
|
|
45
|
+
destination got:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
TS2416: Property 'write' in type 'LokiDestination' is not assignable to the
|
|
49
|
+
same property in base type 'ILogDestination'.
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`ILogDestination` is this library's extension point, so the documentation was
|
|
53
|
+
teaching the wrong shape for the one thing consumers are meant to implement.
|
|
54
|
+
`RollingFileDestination` compounded it by re-serializing a payload that already
|
|
55
|
+
**is** newline-terminated JSON — anyone casting past the type error would have
|
|
56
|
+
written double-encoded lines to disk.
|
|
57
|
+
|
|
58
|
+
Each example now does what its own job requires rather than all three doing the
|
|
59
|
+
same thing: Loki buffers the line verbatim, Prisma parses because it stores
|
|
60
|
+
individual columns, rolling-file writes it through untouched.
|
|
61
|
+
|
|
62
|
+
- **The request-id middleware typed `req` as the global DOM `Request`**, whose
|
|
63
|
+
`headers` is a `Headers` instance and cannot be indexed (`TS7052`). `1.0.4`
|
|
64
|
+
introduced `LoggableRequest` and `LoggableResponse` for exactly this and exports
|
|
65
|
+
them; the README never adopted them. It does now, and narrows the
|
|
66
|
+
`string | string[]` a repeated header produces.
|
|
67
|
+
|
|
68
|
+
### Added
|
|
69
|
+
|
|
70
|
+
- **`pnpm check:published`** — verifies that the README's links resolve, that its
|
|
71
|
+
TypeScript snippets and the type tests compile against the built package, and
|
|
72
|
+
that every `v*.*.*` tag has a `## [x.y.z]` CHANGELOG section. It resolves the
|
|
73
|
+
package through its `exports` map into `dist/`, which `test:types` cannot do:
|
|
74
|
+
that one maps the package to `./src` through tsconfig `paths` and so never
|
|
75
|
+
compiles what the README claims. Both defects above were found by it.
|
|
76
|
+
|
|
77
|
+
Runs in CI, in `release.yml`, and inside `prepublishOnly`.
|
|
78
|
+
|
|
79
|
+
## [1.0.4] - 2026-07-29
|
|
80
|
+
|
|
81
|
+
### Fixed
|
|
82
|
+
|
|
83
|
+
- **The published declarations no longer depend on Express's types.** The HTTP
|
|
84
|
+
logging interceptor, the exception filter and the request-id middleware typed
|
|
85
|
+
their signatures with `Request` / `Response` / `NextFunction` from `express`,
|
|
86
|
+
so `dist/server/index.d.ts` imported from `express` and a consumer compiling
|
|
87
|
+
with `skipLibCheck: false` and no express types installed hit
|
|
88
|
+
`error TS2307: Cannot find module 'express'`. Present since 1.0.0; the imports
|
|
89
|
+
were `import type` only, so runtime was never affected and no express code was
|
|
90
|
+
ever bundled.
|
|
91
|
+
|
|
92
|
+
They are now typed by structural contracts declaring exactly the members this
|
|
93
|
+
package reads — `LoggableRequest` (`headers`, `method`, `url`, `ip`,
|
|
94
|
+
`user?.id`), `LoggableResponse` (`statusCode`, `setHeader`, `status().json()`)
|
|
95
|
+
and `NextHandler`. The emitted declarations import only `@nestjs/common`,
|
|
96
|
+
`pino` and `rxjs`, all declared peers.
|
|
97
|
+
|
|
98
|
+
**Not a breaking change:** an Express request and response satisfy the
|
|
99
|
+
contracts structurally, so existing call sites keep compiling with no cast —
|
|
100
|
+
the parameter types only widen what is accepted. The internal
|
|
101
|
+
`RequestWithUser` alias is gone, replaced by `LoggableRequest`; it was never
|
|
102
|
+
exported.
|
|
103
|
+
|
|
104
|
+
### Added
|
|
105
|
+
|
|
106
|
+
- `LoggableRequest`, `LoggableResponse`, `NextHandler` and `IncomingHeaders` are
|
|
107
|
+
exported from the package root: they type `RequestIdMiddleware.use()`, so a
|
|
108
|
+
consumer invoking it directly (in a test, say) can name them.
|
|
109
|
+
|
|
110
|
+
### Removed
|
|
111
|
+
|
|
112
|
+
- The `@types/express` optional peer dependency added in 1.0.3. With the
|
|
113
|
+
declarations self-contained it serves no purpose — nothing in the published
|
|
114
|
+
types names an express symbol. It stays a devDependency: the specs build
|
|
115
|
+
express-shaped mocks deliberately, to prove a real express request and
|
|
116
|
+
response still satisfy the contracts.
|
|
117
|
+
|
|
14
118
|
## [1.0.3] - 2026-07-29
|
|
15
119
|
|
|
16
120
|
### Fixed
|
|
@@ -148,7 +252,9 @@ published `dist/` is identical — no runtime behaviour changes for consumers.
|
|
|
148
252
|
- Professional CI suite: `ci.yml`, `bench.yml`, `codeql.yml`, `scorecard.yml`,
|
|
149
253
|
`release.yml`, Dependabot, and issue templates
|
|
150
254
|
|
|
151
|
-
[Unreleased]: https://github.com/bymaxone/nest-logger/compare/v1.0.
|
|
255
|
+
[Unreleased]: https://github.com/bymaxone/nest-logger/compare/v1.0.5...HEAD
|
|
256
|
+
[1.0.5]: https://github.com/bymaxone/nest-logger/compare/v1.0.4...v1.0.5
|
|
257
|
+
[1.0.4]: https://github.com/bymaxone/nest-logger/compare/v1.0.3...v1.0.4
|
|
152
258
|
[1.0.3]: https://github.com/bymaxone/nest-logger/compare/v1.0.2...v1.0.3
|
|
153
259
|
[1.0.2]: https://github.com/bymaxone/nest-logger/compare/v1.0.1...v1.0.2
|
|
154
260
|
[1.0.1]: https://github.com/bymaxone/nest-logger/compare/v1.0.0...v1.0.1
|
package/README.md
CHANGED
|
@@ -144,15 +144,13 @@ pnpm add -D pino-pretty
|
|
|
144
144
|
# Optional — OpenTelemetry trace correlation
|
|
145
145
|
pnpm add @opentelemetry/api @opentelemetry/sdk-node
|
|
146
146
|
|
|
147
|
-
# Optional — express types, only if you compile with `skipLibCheck: false`
|
|
148
|
-
pnpm add -D @types/express
|
|
149
147
|
```
|
|
150
148
|
|
|
151
149
|
> [!NOTE]
|
|
152
150
|
> `@opentelemetry/api` is resolved lazily when the Pino instance is built. When it is absent the trace mixin silently steps aside — the logger never fails to start, and never warns, over an optional peer.
|
|
153
151
|
|
|
154
152
|
> [!NOTE]
|
|
155
|
-
>
|
|
153
|
+
> The published declarations depend on no HTTP framework's types. The HTTP interceptor, the exception filter and the request-id middleware are typed by structural contracts (`LoggableRequest`, `LoggableResponse`, `NextHandler`) that an Express request and response satisfy as-is, so nothing extra is needed even when you compile with `skipLibCheck: false`. The emitted `.d.ts` imports only `@nestjs/common`, `pino` and `rxjs`.
|
|
156
154
|
|
|
157
155
|
### 2. Register the module
|
|
158
156
|
|
|
@@ -339,15 +337,21 @@ Once active, every log entry automatically carries `traceId`, `spanId`, and `tra
|
|
|
339
337
|
```typescript
|
|
340
338
|
// request-id.middleware.ts
|
|
341
339
|
import { Injectable, NestMiddleware } from '@nestjs/common'
|
|
342
|
-
import {
|
|
340
|
+
import {
|
|
341
|
+
LogContextService,
|
|
342
|
+
type LoggableRequest,
|
|
343
|
+
type LoggableResponse
|
|
344
|
+
} from '@bymax-one/nest-logger'
|
|
343
345
|
import { randomUUID } from 'node:crypto'
|
|
344
346
|
|
|
345
347
|
@Injectable()
|
|
346
348
|
export class RequestIdMiddleware implements NestMiddleware {
|
|
347
349
|
constructor(private readonly logContext: LogContextService) {}
|
|
348
350
|
|
|
349
|
-
use(req:
|
|
350
|
-
|
|
351
|
+
use(req: LoggableRequest, res: LoggableResponse, next: () => void) {
|
|
352
|
+
// A header can arrive repeated, so its type is `string | string[]`.
|
|
353
|
+
const raw = req.headers['x-request-id']
|
|
354
|
+
const requestId = (Array.isArray(raw) ? raw[0] : raw) ?? `r_${randomUUID()}`
|
|
351
355
|
this.logContext.run({ requestId }, next)
|
|
352
356
|
}
|
|
353
357
|
}
|
|
@@ -441,7 +445,8 @@ export class LokiDestination implements ILogDestination {
|
|
|
441
445
|
private readonly url: string
|
|
442
446
|
private readonly headers: Record<string, string>
|
|
443
447
|
private readonly labels: Record<string, string>
|
|
444
|
-
|
|
448
|
+
// Serialized lines, not objects — `write` receives the payload already encoded.
|
|
449
|
+
private buffer: string[] = []
|
|
445
450
|
private flushTimer?: NodeJS.Timeout
|
|
446
451
|
|
|
447
452
|
constructor(opts: {
|
|
@@ -468,8 +473,10 @@ export class LokiDestination implements ILogDestination {
|
|
|
468
473
|
await this.flush()
|
|
469
474
|
}
|
|
470
475
|
|
|
471
|
-
|
|
472
|
-
|
|
476
|
+
// `payload` already IS the serialized entry, newline-terminated. Buffer it as
|
|
477
|
+
// it arrives; parse only where a field is genuinely needed, as `flush` does.
|
|
478
|
+
write(payload: string): void {
|
|
479
|
+
this.buffer.push(payload)
|
|
473
480
|
if (this.buffer.length >= 100) void this.flush()
|
|
474
481
|
}
|
|
475
482
|
|
|
@@ -480,7 +487,10 @@ export class LokiDestination implements ILogDestination {
|
|
|
480
487
|
streams: [
|
|
481
488
|
{
|
|
482
489
|
stream: this.labels,
|
|
483
|
-
values: batch.map((
|
|
490
|
+
values: batch.map((line) => [
|
|
491
|
+
String(BigInt((JSON.parse(line) as LogEntry).time) * 1_000_000n),
|
|
492
|
+
line.trimEnd()
|
|
493
|
+
])
|
|
484
494
|
}
|
|
485
495
|
]
|
|
486
496
|
})
|
|
@@ -522,7 +532,10 @@ export class PrismaLogDestination implements ILogDestination {
|
|
|
522
532
|
|
|
523
533
|
constructor(private readonly prisma: PrismaClient) {}
|
|
524
534
|
|
|
525
|
-
write(
|
|
535
|
+
write(payload: string): void {
|
|
536
|
+
// This destination stores individual columns, so it is one of the few that
|
|
537
|
+
// has to parse. A sink that forwards the line verbatim should not.
|
|
538
|
+
const entry = JSON.parse(payload) as LogEntry
|
|
526
539
|
void this.prisma.applicationLog.create({
|
|
527
540
|
data: {
|
|
528
541
|
level: entry.level,
|
|
@@ -559,8 +572,9 @@ export class RollingFileDestination implements ILogDestination {
|
|
|
559
572
|
await new Promise<void>((resolve) => this.stream?.end(resolve))
|
|
560
573
|
}
|
|
561
574
|
|
|
562
|
-
write(
|
|
563
|
-
|
|
575
|
+
write(payload: string): void {
|
|
576
|
+
// Already newline-terminated JSON — re-serializing would double-encode it.
|
|
577
|
+
this.stream?.write(payload)
|
|
564
578
|
}
|
|
565
579
|
}
|
|
566
580
|
```
|
|
@@ -808,7 +822,8 @@ type LogEntry = {
|
|
|
808
822
|
interface ILogDestination {
|
|
809
823
|
readonly name: string
|
|
810
824
|
readonly minLevel?: LogLevel
|
|
811
|
-
|
|
825
|
+
/** `payload` is a newline-terminated JSON entry, UTF-8 encoded — not an object. */
|
|
826
|
+
write(payload: string): void | Promise<void>
|
|
812
827
|
onInit?(): Promise<void>
|
|
813
828
|
onShutdown?(): Promise<void>
|
|
814
829
|
}
|
package/dist/server/index.cjs
CHANGED
|
@@ -1363,8 +1363,8 @@ exports.RequestIdMiddleware = class RequestIdMiddleware {
|
|
|
1363
1363
|
* Echo or mint the `x-request-id`, expose it on the response, and run the
|
|
1364
1364
|
* remainder of the request inside a fresh log-context scope.
|
|
1365
1365
|
*
|
|
1366
|
-
* @param req - The incoming Express
|
|
1367
|
-
* @param res - The outgoing Express
|
|
1366
|
+
* @param req - The incoming request (Express satisfies the contract).
|
|
1367
|
+
* @param res - The outgoing response (Express satisfies the contract).
|
|
1368
1368
|
* @param next - The next handler in the chain.
|
|
1369
1369
|
*/
|
|
1370
1370
|
use(req, res, next) {
|
package/dist/server/index.d.cts
CHANGED
|
@@ -2,7 +2,6 @@ import * as _nestjs_common from '@nestjs/common';
|
|
|
2
2
|
import { ModuleMetadata, Type, DynamicModule, INestApplication, LoggerService, OnApplicationShutdown, ExceptionFilter, ArgumentsHost, NestInterceptor, ExecutionContext, CallHandler, MiddlewareConsumer, NestMiddleware, CustomDecorator } from '@nestjs/common';
|
|
3
3
|
import { Logger } from 'pino';
|
|
4
4
|
import { Observable } from 'rxjs';
|
|
5
|
-
import { Request, Response, NextFunction } from 'express';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Pino log levels (mirrored to NestJS-compatible names).
|
|
@@ -753,6 +752,66 @@ declare class HttpLoggingInterceptor implements NestInterceptor {
|
|
|
753
752
|
*/
|
|
754
753
|
declare function applyRequestIdMiddleware(consumer: MiddlewareConsumer, routes?: string): void;
|
|
755
754
|
|
|
755
|
+
/**
|
|
756
|
+
* Minimal HTTP request/response contracts the logging layer reads.
|
|
757
|
+
*
|
|
758
|
+
* Layer: server/interfaces — exported structural contracts shared by the HTTP
|
|
759
|
+
* logging interceptor, the exception filter and the request-id middleware. They
|
|
760
|
+
* are part of the public API: they appear in those components' published
|
|
761
|
+
* signatures, so a consumer calling `RequestIdMiddleware.use()` directly needs
|
|
762
|
+
* to be able to name them. Declared structurally, listing only the members
|
|
763
|
+
* those three actually touch, so the published declarations depend on no HTTP
|
|
764
|
+
* framework's types. Naming Express's
|
|
765
|
+
* `Request`/`Response` here would make the emitted `.d.ts` import from
|
|
766
|
+
* `express`, forcing every consumer that compiles with `skipLibCheck: false` to
|
|
767
|
+
* install `@types/express` whether or not they use those signatures.
|
|
768
|
+
*
|
|
769
|
+
* The shapes mirror what the Express adapter provides — `status().json()` and
|
|
770
|
+
* `setHeader()` are Express/Node response methods — so this is not an
|
|
771
|
+
* adapter-agnostic abstraction; a Fastify adapter remains unsupported. An
|
|
772
|
+
* Express request and response satisfy these contracts structurally, with no
|
|
773
|
+
* cast at the call site.
|
|
774
|
+
*/
|
|
775
|
+
/** Raw inbound headers, as the HTTP layer delivers them. */
|
|
776
|
+
type IncomingHeaders = Record<string, string | string[] | undefined>;
|
|
777
|
+
/**
|
|
778
|
+
* The inbound request members the logging layer reads.
|
|
779
|
+
*
|
|
780
|
+
* `user` is populated by upstream auth in the consumer app (a guard or
|
|
781
|
+
* middleware); only `id` is ever read, for the acting-user log field.
|
|
782
|
+
*/
|
|
783
|
+
interface LoggableRequest {
|
|
784
|
+
/** Inbound headers — read for correlation ids and the user agent. */
|
|
785
|
+
readonly headers: IncomingHeaders;
|
|
786
|
+
/** HTTP verb, logged as-is. */
|
|
787
|
+
readonly method: string;
|
|
788
|
+
/** Request target, normalized and query-stripped before it reaches a log. */
|
|
789
|
+
readonly url: string;
|
|
790
|
+
/** Client address when the adapter resolves one. */
|
|
791
|
+
readonly ip?: string | undefined;
|
|
792
|
+
/** Authenticated principal, when upstream auth attached one. */
|
|
793
|
+
readonly user?: {
|
|
794
|
+
readonly id?: string | undefined;
|
|
795
|
+
} | undefined;
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* The outbound response members the logging layer writes or reads: the status
|
|
799
|
+
* for the terminal log entry, `setHeader` to echo the correlation id, and the
|
|
800
|
+
* chainable `status().json()` used by the exception filter to emit a body.
|
|
801
|
+
*/
|
|
802
|
+
interface LoggableResponse {
|
|
803
|
+
/** Status code, read when logging the completed request. */
|
|
804
|
+
readonly statusCode: number;
|
|
805
|
+
/** Sets a response header (used for the correlation id). */
|
|
806
|
+
setHeader(name: string, value: string): void;
|
|
807
|
+
/** Selects the status code and returns the chainable body writer. */
|
|
808
|
+
status(code: number): {
|
|
809
|
+
json(body: unknown): unknown;
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
/** Continuation passed to a middleware; invoked with no arguments here. */
|
|
813
|
+
type NextHandler = () => void;
|
|
814
|
+
|
|
756
815
|
/**
|
|
757
816
|
* Reads (or generates) the correlation id and starts the per-request log
|
|
758
817
|
* context scope.
|
|
@@ -783,11 +842,11 @@ declare class RequestIdMiddleware implements NestMiddleware {
|
|
|
783
842
|
* Echo or mint the `x-request-id`, expose it on the response, and run the
|
|
784
843
|
* remainder of the request inside a fresh log-context scope.
|
|
785
844
|
*
|
|
786
|
-
* @param req - The incoming Express
|
|
787
|
-
* @param res - The outgoing Express
|
|
845
|
+
* @param req - The incoming request (Express satisfies the contract).
|
|
846
|
+
* @param res - The outgoing response (Express satisfies the contract).
|
|
788
847
|
* @param next - The next handler in the chain.
|
|
789
848
|
*/
|
|
790
|
-
use(req:
|
|
849
|
+
use(req: LoggableRequest, res: LoggableResponse, next: NextHandler): void;
|
|
791
850
|
}
|
|
792
851
|
|
|
793
852
|
/**
|
|
@@ -950,4 +1009,4 @@ declare const RESERVED_LOG_KEYS: Readonly<{
|
|
|
950
1009
|
readonly LOGGER_ENTRY_TRUNCATED: "LOGGER_ENTRY_TRUNCATED";
|
|
951
1010
|
}>;
|
|
952
1011
|
|
|
953
|
-
export { BymaxLoggerModule, type BymaxLoggerModuleAsyncOptions, type BymaxLoggerModuleOptions, type BymaxLoggerModuleOptionsFactory, DEFAULT_REDACT_PATHS, DefaultStdoutDestination, HttpExceptionFilter, HttpLoggingInterceptor, type HttpOptions, type ILogDestination, 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 OtelOptions, PinoLoggerService, PrettyDevDestination, RESERVED_LOG_KEYS, RequestIdMiddleware, type ResolvedBymaxLoggerModuleOptions, type ServiceMetadata, applyRequestIdMiddleware };
|
|
1012
|
+
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 };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ import * as _nestjs_common from '@nestjs/common';
|
|
|
2
2
|
import { ModuleMetadata, Type, DynamicModule, INestApplication, LoggerService, OnApplicationShutdown, ExceptionFilter, ArgumentsHost, NestInterceptor, ExecutionContext, CallHandler, MiddlewareConsumer, NestMiddleware, CustomDecorator } from '@nestjs/common';
|
|
3
3
|
import { Logger } from 'pino';
|
|
4
4
|
import { Observable } from 'rxjs';
|
|
5
|
-
import { Request, Response, NextFunction } from 'express';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Pino log levels (mirrored to NestJS-compatible names).
|
|
@@ -753,6 +752,66 @@ declare class HttpLoggingInterceptor implements NestInterceptor {
|
|
|
753
752
|
*/
|
|
754
753
|
declare function applyRequestIdMiddleware(consumer: MiddlewareConsumer, routes?: string): void;
|
|
755
754
|
|
|
755
|
+
/**
|
|
756
|
+
* Minimal HTTP request/response contracts the logging layer reads.
|
|
757
|
+
*
|
|
758
|
+
* Layer: server/interfaces — exported structural contracts shared by the HTTP
|
|
759
|
+
* logging interceptor, the exception filter and the request-id middleware. They
|
|
760
|
+
* are part of the public API: they appear in those components' published
|
|
761
|
+
* signatures, so a consumer calling `RequestIdMiddleware.use()` directly needs
|
|
762
|
+
* to be able to name them. Declared structurally, listing only the members
|
|
763
|
+
* those three actually touch, so the published declarations depend on no HTTP
|
|
764
|
+
* framework's types. Naming Express's
|
|
765
|
+
* `Request`/`Response` here would make the emitted `.d.ts` import from
|
|
766
|
+
* `express`, forcing every consumer that compiles with `skipLibCheck: false` to
|
|
767
|
+
* install `@types/express` whether or not they use those signatures.
|
|
768
|
+
*
|
|
769
|
+
* The shapes mirror what the Express adapter provides — `status().json()` and
|
|
770
|
+
* `setHeader()` are Express/Node response methods — so this is not an
|
|
771
|
+
* adapter-agnostic abstraction; a Fastify adapter remains unsupported. An
|
|
772
|
+
* Express request and response satisfy these contracts structurally, with no
|
|
773
|
+
* cast at the call site.
|
|
774
|
+
*/
|
|
775
|
+
/** Raw inbound headers, as the HTTP layer delivers them. */
|
|
776
|
+
type IncomingHeaders = Record<string, string | string[] | undefined>;
|
|
777
|
+
/**
|
|
778
|
+
* The inbound request members the logging layer reads.
|
|
779
|
+
*
|
|
780
|
+
* `user` is populated by upstream auth in the consumer app (a guard or
|
|
781
|
+
* middleware); only `id` is ever read, for the acting-user log field.
|
|
782
|
+
*/
|
|
783
|
+
interface LoggableRequest {
|
|
784
|
+
/** Inbound headers — read for correlation ids and the user agent. */
|
|
785
|
+
readonly headers: IncomingHeaders;
|
|
786
|
+
/** HTTP verb, logged as-is. */
|
|
787
|
+
readonly method: string;
|
|
788
|
+
/** Request target, normalized and query-stripped before it reaches a log. */
|
|
789
|
+
readonly url: string;
|
|
790
|
+
/** Client address when the adapter resolves one. */
|
|
791
|
+
readonly ip?: string | undefined;
|
|
792
|
+
/** Authenticated principal, when upstream auth attached one. */
|
|
793
|
+
readonly user?: {
|
|
794
|
+
readonly id?: string | undefined;
|
|
795
|
+
} | undefined;
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* The outbound response members the logging layer writes or reads: the status
|
|
799
|
+
* for the terminal log entry, `setHeader` to echo the correlation id, and the
|
|
800
|
+
* chainable `status().json()` used by the exception filter to emit a body.
|
|
801
|
+
*/
|
|
802
|
+
interface LoggableResponse {
|
|
803
|
+
/** Status code, read when logging the completed request. */
|
|
804
|
+
readonly statusCode: number;
|
|
805
|
+
/** Sets a response header (used for the correlation id). */
|
|
806
|
+
setHeader(name: string, value: string): void;
|
|
807
|
+
/** Selects the status code and returns the chainable body writer. */
|
|
808
|
+
status(code: number): {
|
|
809
|
+
json(body: unknown): unknown;
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
/** Continuation passed to a middleware; invoked with no arguments here. */
|
|
813
|
+
type NextHandler = () => void;
|
|
814
|
+
|
|
756
815
|
/**
|
|
757
816
|
* Reads (or generates) the correlation id and starts the per-request log
|
|
758
817
|
* context scope.
|
|
@@ -783,11 +842,11 @@ declare class RequestIdMiddleware implements NestMiddleware {
|
|
|
783
842
|
* Echo or mint the `x-request-id`, expose it on the response, and run the
|
|
784
843
|
* remainder of the request inside a fresh log-context scope.
|
|
785
844
|
*
|
|
786
|
-
* @param req - The incoming Express
|
|
787
|
-
* @param res - The outgoing Express
|
|
845
|
+
* @param req - The incoming request (Express satisfies the contract).
|
|
846
|
+
* @param res - The outgoing response (Express satisfies the contract).
|
|
788
847
|
* @param next - The next handler in the chain.
|
|
789
848
|
*/
|
|
790
|
-
use(req:
|
|
849
|
+
use(req: LoggableRequest, res: LoggableResponse, next: NextHandler): void;
|
|
791
850
|
}
|
|
792
851
|
|
|
793
852
|
/**
|
|
@@ -950,4 +1009,4 @@ declare const RESERVED_LOG_KEYS: Readonly<{
|
|
|
950
1009
|
readonly LOGGER_ENTRY_TRUNCATED: "LOGGER_ENTRY_TRUNCATED";
|
|
951
1010
|
}>;
|
|
952
1011
|
|
|
953
|
-
export { BymaxLoggerModule, type BymaxLoggerModuleAsyncOptions, type BymaxLoggerModuleOptions, type BymaxLoggerModuleOptionsFactory, DEFAULT_REDACT_PATHS, DefaultStdoutDestination, HttpExceptionFilter, HttpLoggingInterceptor, type HttpOptions, type ILogDestination, 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 OtelOptions, PinoLoggerService, PrettyDevDestination, RESERVED_LOG_KEYS, RequestIdMiddleware, type ResolvedBymaxLoggerModuleOptions, type ServiceMetadata, applyRequestIdMiddleware };
|
|
1012
|
+
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 };
|
package/dist/server/index.mjs
CHANGED
|
@@ -1357,8 +1357,8 @@ var RequestIdMiddleware = class {
|
|
|
1357
1357
|
* Echo or mint the `x-request-id`, expose it on the response, and run the
|
|
1358
1358
|
* remainder of the request inside a fresh log-context scope.
|
|
1359
1359
|
*
|
|
1360
|
-
* @param req - The incoming Express
|
|
1361
|
-
* @param res - The outgoing Express
|
|
1360
|
+
* @param req - The incoming request (Express satisfies the contract).
|
|
1361
|
+
* @param res - The outgoing response (Express satisfies the contract).
|
|
1362
1362
|
* @param next - The next handler in the chain.
|
|
1363
1363
|
*/
|
|
1364
1364
|
use(req, res, next) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bymax-one/nest-logger",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
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",
|
|
@@ -64,10 +64,9 @@
|
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {},
|
|
66
66
|
"peerDependencies": {
|
|
67
|
-
"@nestjs/common": "^11.0.
|
|
68
|
-
"@nestjs/core": "^11.
|
|
67
|
+
"@nestjs/common": "^11.0.16",
|
|
68
|
+
"@nestjs/core": "^11.1.18",
|
|
69
69
|
"@opentelemetry/api": "^1.9.0",
|
|
70
|
-
"@types/express": "^4.17.21 || ^5.0.0",
|
|
71
70
|
"pino": "^10.0.0",
|
|
72
71
|
"pino-pretty": "^13.0.0",
|
|
73
72
|
"reflect-metadata": "^0.2.0",
|
|
@@ -77,9 +76,6 @@
|
|
|
77
76
|
"@opentelemetry/api": {
|
|
78
77
|
"optional": true
|
|
79
78
|
},
|
|
80
|
-
"@types/express": {
|
|
81
|
-
"optional": true
|
|
82
|
-
},
|
|
83
79
|
"pino-pretty": {
|
|
84
80
|
"optional": true
|
|
85
81
|
}
|
|
@@ -146,23 +142,24 @@
|
|
|
146
142
|
"registry": "https://registry.npmjs.org/"
|
|
147
143
|
},
|
|
148
144
|
"scripts": {
|
|
145
|
+
"bench": "node --expose-gc --import tsx bench/throughput.bench.ts",
|
|
149
146
|
"build": "pnpm clean && tsup",
|
|
147
|
+
"check:exports": "attw --pack .",
|
|
148
|
+
"check:published": "node scripts/check-published-surface.mjs",
|
|
149
|
+
"clean": "rm -rf dist coverage",
|
|
150
150
|
"lint": "eslint src",
|
|
151
151
|
"lint:fix": "eslint src --fix",
|
|
152
|
-
"test": "jest",
|
|
153
|
-
"test:cov": "jest --coverage",
|
|
154
|
-
"test:watch": "jest --watch",
|
|
155
|
-
"test:e2e": "jest --config jest.e2e.config.ts",
|
|
156
|
-
"test:all": "pnpm test && pnpm test:e2e",
|
|
157
|
-
"test:cov:all": "jest --config jest.coverage.config.ts --coverage",
|
|
158
152
|
"mutation": "stryker run",
|
|
159
|
-
"mutation:incremental": "stryker run --incremental",
|
|
160
153
|
"mutation:dry-run": "stryker run --dryRunOnly",
|
|
161
|
-
"
|
|
162
|
-
"
|
|
154
|
+
"mutation:incremental": "stryker run --incremental",
|
|
155
|
+
"release": "pnpm publish --provenance",
|
|
163
156
|
"size": "node scripts/check-size.mjs",
|
|
164
|
-
"
|
|
165
|
-
"
|
|
166
|
-
"
|
|
157
|
+
"test": "jest",
|
|
158
|
+
"test:all": "pnpm test && pnpm test:e2e",
|
|
159
|
+
"test:cov": "jest --coverage",
|
|
160
|
+
"test:cov:all": "jest --config jest.coverage.config.ts --coverage",
|
|
161
|
+
"test:e2e": "jest --config jest.e2e.config.ts",
|
|
162
|
+
"test:watch": "jest --watch",
|
|
163
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.server.json"
|
|
167
164
|
}
|
|
168
165
|
}
|