@bymax-one/nest-logger 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +74 -0
- package/LICENSE +21 -0
- package/README.md +797 -0
- package/dist/server/index.cjs +1470 -0
- package/dist/server/index.d.cts +953 -0
- package/dist/server/index.d.ts +953 -0
- package/dist/server/index.mjs +1452 -0
- package/dist/shared/index.cjs +27 -0
- package/dist/shared/index.d.cts +120 -0
- package/dist/shared/index.d.ts +120 -0
- package/dist/shared/index.mjs +24 -0
- package/package.json +141 -0
|
@@ -0,0 +1,953 @@
|
|
|
1
|
+
import * as _nestjs_common from '@nestjs/common';
|
|
2
|
+
import { ModuleMetadata, Type, DynamicModule, INestApplication, LoggerService, OnApplicationShutdown, ExceptionFilter, ArgumentsHost, NestInterceptor, ExecutionContext, CallHandler, MiddlewareConsumer, NestMiddleware, CustomDecorator } from '@nestjs/common';
|
|
3
|
+
import { Logger } from 'pino';
|
|
4
|
+
import { Observable } from 'rxjs';
|
|
5
|
+
import { Request, Response, NextFunction } from 'express';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Pino log levels (mirrored to NestJS-compatible names).
|
|
9
|
+
*
|
|
10
|
+
* Numeric mapping (Pino):
|
|
11
|
+
* - `fatal` 60
|
|
12
|
+
* - `error` 50
|
|
13
|
+
* - `warn` 40
|
|
14
|
+
* - `info` 30
|
|
15
|
+
* - `debug` 20
|
|
16
|
+
* - `trace` 10
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* const level: LogLevel = 'info'
|
|
20
|
+
*/
|
|
21
|
+
type LogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Pluggable destination contract for the logger.
|
|
25
|
+
*
|
|
26
|
+
* Layer: server/interfaces — implemented by every concrete sink
|
|
27
|
+
* (`DefaultStdoutDestination`, future HTTP / file / queue destinations).
|
|
28
|
+
* The library owns the JSON serialization; implementations only forward
|
|
29
|
+
* the already-encoded payload.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Pluggable destination for log entries.
|
|
34
|
+
*
|
|
35
|
+
* Implementations receive the final JSON-serialized payload (with trailing
|
|
36
|
+
* newline) and write it wherever they want — file, network, queue, etc.
|
|
37
|
+
*
|
|
38
|
+
* Implementations MUST be non-blocking and tolerant to errors. A destination
|
|
39
|
+
* that throws or hangs MUST NOT break the application — the library's
|
|
40
|
+
* `DestinationRegistry` catches errors and emits a meta-log
|
|
41
|
+
* (`LOGGER_DESTINATION_WRITE_FAILED`).
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* class FileDestination implements ILogDestination {
|
|
45
|
+
* readonly name = 'file'
|
|
46
|
+
* write(payload: string): void {
|
|
47
|
+
* fs.appendFileSync('/var/log/app.log', payload)
|
|
48
|
+
* }
|
|
49
|
+
* }
|
|
50
|
+
*/
|
|
51
|
+
interface ILogDestination {
|
|
52
|
+
/** Unique identifier — used in error logs and registry lookups. */
|
|
53
|
+
readonly name: string;
|
|
54
|
+
/**
|
|
55
|
+
* Minimum log level this destination accepts. Entries below this level
|
|
56
|
+
* are not written. Undefined means "accept everything".
|
|
57
|
+
*/
|
|
58
|
+
readonly minLevel?: LogLevel;
|
|
59
|
+
/**
|
|
60
|
+
* Write a single log entry. Payload is the already-serialized JSON string
|
|
61
|
+
* (UTF-8, terminated with `\n`).
|
|
62
|
+
*
|
|
63
|
+
* Implementations may:
|
|
64
|
+
* - Write synchronously (e.g., `process.stdout`)
|
|
65
|
+
* - Buffer and flush periodically (e.g., HTTP batching)
|
|
66
|
+
* - Return a Promise for async I/O
|
|
67
|
+
*
|
|
68
|
+
* Errors thrown here are caught by the library; the destination may be
|
|
69
|
+
* temporarily skipped if it consistently fails.
|
|
70
|
+
*
|
|
71
|
+
* @param payload — Newline-terminated JSON entry, UTF-8 encoded.
|
|
72
|
+
*/
|
|
73
|
+
write(payload: string): void | Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Optional lifecycle hook — called once during NestJS module init.
|
|
76
|
+
* Use for opening connections, allocating buffers, scheduling flushers.
|
|
77
|
+
*/
|
|
78
|
+
onInit?(): void | Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* Optional lifecycle hook — called during NestJS `onApplicationShutdown`.
|
|
81
|
+
* MUST flush any pending writes and close resources.
|
|
82
|
+
*
|
|
83
|
+
* The library awaits this — graceful shutdown blocks until all destinations
|
|
84
|
+
* return.
|
|
85
|
+
*/
|
|
86
|
+
onShutdown?(): void | Promise<void>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Service metadata propagated to every log entry.
|
|
91
|
+
*
|
|
92
|
+
* Aligned with OpenTelemetry semantic conventions for service identity:
|
|
93
|
+
* {@link https://opentelemetry.io/docs/specs/semconv/resource/#service}
|
|
94
|
+
*/
|
|
95
|
+
interface ServiceMetadata {
|
|
96
|
+
/** OTel attribute `service.name` — stable identifier of the running service. */
|
|
97
|
+
readonly name: string;
|
|
98
|
+
/** OTel attribute `service.version` — semver, commit SHA, or build tag. */
|
|
99
|
+
readonly version: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Public module-options interfaces for `BymaxLoggerModule`.
|
|
104
|
+
*
|
|
105
|
+
* Layer: server/interfaces — the contracts consumed by `forRoot` /
|
|
106
|
+
* `forRootAsync` plus the nested `HttpOptions` / `OtelOptions` shapes and
|
|
107
|
+
* the async factory pair. The synchronous and asynchronous variants are
|
|
108
|
+
* intentionally separate types (not a union) so the dynamic-module helper
|
|
109
|
+
* can branch on shape without runtime discrimination.
|
|
110
|
+
*/
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* HTTP module configuration — used by the optional HTTP interceptor and filter.
|
|
114
|
+
*
|
|
115
|
+
* Every field is optional; defaults are applied by `applyDefaults`.
|
|
116
|
+
*/
|
|
117
|
+
interface HttpOptions {
|
|
118
|
+
/** Mount the HTTP interceptor + filter. Default: false. */
|
|
119
|
+
isEnabled?: boolean;
|
|
120
|
+
/** Capture unhandled HTTP exceptions and emit `HTTP_EXCEPTION_UNHANDLED`. Default: true. */
|
|
121
|
+
shouldCaptureExceptions?: boolean;
|
|
122
|
+
/** Generate `requestId` when the request header is absent. Default: true. */
|
|
123
|
+
shouldGenerateRequestId?: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Paths that bypass HTTP logging (health checks, metrics). Defaults:
|
|
126
|
+
* `/health`, `/metrics`.
|
|
127
|
+
*
|
|
128
|
+
* SECURITY: each pattern is `.test()`-ed against the (attacker-controllable)
|
|
129
|
+
* request path on every request. Supply only anchored, linear-time regexes
|
|
130
|
+
* (e.g. `/^\/health$/`); a pattern with catastrophic backtracking would turn a
|
|
131
|
+
* crafted URL into a ReDoS vector. The defaults are anchored and safe.
|
|
132
|
+
*/
|
|
133
|
+
excludePaths?: readonly RegExp[];
|
|
134
|
+
/** Header name carrying the tenant identifier. Default: `x-tenant-id`. */
|
|
135
|
+
tenantIdHeader?: string;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* OpenTelemetry integration tuning — applied by `TraceContextMixin` when the
|
|
139
|
+
* `@opentelemetry/api` peer dependency is installed.
|
|
140
|
+
*/
|
|
141
|
+
interface OtelOptions {
|
|
142
|
+
/** Inject the active span's trace/span IDs into every log entry. Default: true. */
|
|
143
|
+
shouldAutoInjectTraceContext?: boolean;
|
|
144
|
+
/**
|
|
145
|
+
* Shortcut that derives sensible defaults for `traceIdField` / `spanIdField` /
|
|
146
|
+
* `traceFlagsField` based on a target casing.
|
|
147
|
+
*
|
|
148
|
+
* - `'camelCase'` (default) → `traceId` / `spanId` / `traceFlags`
|
|
149
|
+
* - `'snake_case'` → `trace_id` / `span_id` / `trace_flags`
|
|
150
|
+
* (OTel Logs Data Model wire format)
|
|
151
|
+
*
|
|
152
|
+
* Individual `*Field` overrides ALWAYS win over the shortcut.
|
|
153
|
+
*/
|
|
154
|
+
fieldFormat?: 'camelCase' | 'snake_case';
|
|
155
|
+
/** Override the field name for the trace ID. */
|
|
156
|
+
traceIdField?: string;
|
|
157
|
+
/** Override the field name for the span ID. */
|
|
158
|
+
spanIdField?: string;
|
|
159
|
+
/** Override the field name for the trace flags. */
|
|
160
|
+
traceFlagsField?: string;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Synchronous configuration for `BymaxLoggerModule.forRoot()`.
|
|
164
|
+
*/
|
|
165
|
+
interface BymaxLoggerModuleOptions {
|
|
166
|
+
/** Mandatory service metadata — emitted on every log entry. */
|
|
167
|
+
service: ServiceMetadata;
|
|
168
|
+
/** Minimum level emitted. Default: `info` in production, `debug` otherwise. */
|
|
169
|
+
level?: LogLevel;
|
|
170
|
+
/** Register the module as `@Global()`. Default: true. */
|
|
171
|
+
isGlobal?: boolean;
|
|
172
|
+
/**
|
|
173
|
+
* @deprecated Not read by the module — a module cannot call `app.useLogger()`
|
|
174
|
+
* itself (it never sees the `INestApplication`). Call
|
|
175
|
+
* `BymaxLoggerModule.useNestLogger(app)` in `main.ts` instead. Default: true.
|
|
176
|
+
*/
|
|
177
|
+
shouldUseAsNestLogger?: boolean;
|
|
178
|
+
/** Additional Pino redact paths — merged with `DEFAULT_REDACT_PATHS`. */
|
|
179
|
+
redactPaths?: readonly string[];
|
|
180
|
+
/** Censor string written in place of redacted values. Default: `[REDACTED]`. */
|
|
181
|
+
redactCensor?: string;
|
|
182
|
+
/** Disable default redact paths (use with caution). Default: false. */
|
|
183
|
+
shouldDisableDefaultRedact?: boolean;
|
|
184
|
+
/** Custom destinations beyond `DefaultStdoutDestination`. */
|
|
185
|
+
destinations?: readonly ILogDestination[];
|
|
186
|
+
/**
|
|
187
|
+
* @deprecated Currently inert — a `true` value does not change output on its
|
|
188
|
+
* own. For pretty local logs add `new PrettyDevDestination()` to
|
|
189
|
+
* `destinations` (and install the optional `pino-pretty` peer dep).
|
|
190
|
+
* Default: `NODE_ENV !== 'production'`.
|
|
191
|
+
*/
|
|
192
|
+
isPretty?: boolean;
|
|
193
|
+
/** HTTP module configuration. */
|
|
194
|
+
http?: HttpOptions;
|
|
195
|
+
/** OpenTelemetry integration tuning. */
|
|
196
|
+
otel?: OtelOptions;
|
|
197
|
+
/** Maximum size in bytes per log entry. Default: 65536. */
|
|
198
|
+
maxEntrySizeBytes?: number;
|
|
199
|
+
/** Custom Pino serializers, merged with defaults. */
|
|
200
|
+
serializers?: Record<string, (input: unknown) => unknown>;
|
|
201
|
+
/** Custom timestamp function. Default: `() => new Date().toISOString()`. */
|
|
202
|
+
timestamp?: () => string;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Fully-resolved options after `applyDefaults` runs.
|
|
206
|
+
*
|
|
207
|
+
* Every optional field is filled, and the nested `http` / `otel` bags are
|
|
208
|
+
* deep-required because {@link applyDefaults} populates each of their fields.
|
|
209
|
+
* This is the precise shape every runtime layer (pino factory, services,
|
|
210
|
+
* middleware) consumes — distinct from the loosely-typed
|
|
211
|
+
* `BymaxLoggerModuleOptions` the consumer supplies.
|
|
212
|
+
*/
|
|
213
|
+
type ResolvedBymaxLoggerModuleOptions = Readonly<Required<Omit<BymaxLoggerModuleOptions, 'http' | 'otel'>> & {
|
|
214
|
+
http: Required<HttpOptions>;
|
|
215
|
+
otel: Required<OtelOptions>;
|
|
216
|
+
}>;
|
|
217
|
+
/**
|
|
218
|
+
* Factory interface used by `useExisting` / `useClass` async wiring.
|
|
219
|
+
*
|
|
220
|
+
* Implement this interface when the module options depend on providers that are
|
|
221
|
+
* already in the DI graph (e.g., a `ConfigService`). Pass the class to
|
|
222
|
+
* `forRootAsync({ useClass: MyFactory })` or the token to `useExisting`.
|
|
223
|
+
*/
|
|
224
|
+
interface BymaxLoggerModuleOptionsFactory {
|
|
225
|
+
/**
|
|
226
|
+
* Produce the synchronous or asynchronous module options.
|
|
227
|
+
*
|
|
228
|
+
* @returns Resolved or promised `BymaxLoggerModuleOptions`.
|
|
229
|
+
* @throws Any error thrown here propagates as a module-initialisation failure.
|
|
230
|
+
*/
|
|
231
|
+
createLoggerOptions(): BymaxLoggerModuleOptions | Promise<BymaxLoggerModuleOptions>;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Asynchronous configuration for `BymaxLoggerModule.forRootAsync()`.
|
|
235
|
+
*
|
|
236
|
+
* Standard NestJS dynamic-module async-options shape — pick `imports` from
|
|
237
|
+
* `ModuleMetadata` to allow re-using the consumer's providers in the factory.
|
|
238
|
+
*/
|
|
239
|
+
interface BymaxLoggerModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
|
|
240
|
+
/**
|
|
241
|
+
* Factory producing the module options. The `unknown[]` parameter shape
|
|
242
|
+
* matches the heterogeneous `inject` array — NestJS resolves each token
|
|
243
|
+
* independently and passes the resolved providers as positional args, so a
|
|
244
|
+
* tighter generic would force every consumer to widen the signature with
|
|
245
|
+
* `as` casts at the call site.
|
|
246
|
+
*/
|
|
247
|
+
useFactory?: (...args: unknown[]) => BymaxLoggerModuleOptions | Promise<BymaxLoggerModuleOptions>;
|
|
248
|
+
/** Provider tokens NestJS must resolve and forward to `useFactory`. */
|
|
249
|
+
inject?: readonly (string | symbol | Type<unknown>)[];
|
|
250
|
+
/** Reuse an existing factory provider already registered in the DI graph. */
|
|
251
|
+
useExisting?: Type<BymaxLoggerModuleOptionsFactory>;
|
|
252
|
+
/** Instantiate a factory class and call its `createLoggerOptions()`. */
|
|
253
|
+
useClass?: Type<BymaxLoggerModuleOptionsFactory>;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Extra (non-option) flags accepted by `forRoot` / `forRootAsync`. */
|
|
257
|
+
interface BymaxLoggerModuleExtras {
|
|
258
|
+
/** Register the module globally. Default: `true`. */
|
|
259
|
+
isGlobal?: boolean;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* `BymaxLoggerModuleBase` — base class providing the generated `forRoot` /
|
|
263
|
+
* `forRootAsync` implementations.
|
|
264
|
+
*
|
|
265
|
+
* `BUILDER_OPTIONS_TOKEN` — internal DI token for the raw (pre-defaulted)
|
|
266
|
+
* consumer options; not intended for injection outside this module.
|
|
267
|
+
*
|
|
268
|
+
* `OPTIONS_TYPE` / `ASYNC_OPTIONS_TYPE` — `typeof`-only sentinels used as
|
|
269
|
+
* parameter types in the static factory overrides; never instantiated at
|
|
270
|
+
* runtime.
|
|
271
|
+
*/
|
|
272
|
+
declare const BymaxLoggerModuleBase: _nestjs_common.ConfigurableModuleCls<BymaxLoggerModuleOptions, "forRoot", "createLoggerOptions", BymaxLoggerModuleExtras>;
|
|
273
|
+
declare const OPTIONS_TYPE: BymaxLoggerModuleOptions & Partial<BymaxLoggerModuleExtras>;
|
|
274
|
+
declare const ASYNC_OPTIONS_TYPE: _nestjs_common.ConfigurableModuleAsyncOptions<BymaxLoggerModuleOptions, "createLoggerOptions"> & Partial<BymaxLoggerModuleExtras>;
|
|
275
|
+
|
|
276
|
+
declare class BymaxLoggerModule extends BymaxLoggerModuleBase {
|
|
277
|
+
/**
|
|
278
|
+
* Synchronous registration.
|
|
279
|
+
*
|
|
280
|
+
* @param options - Logger options (plus the `isGlobal` extra).
|
|
281
|
+
* @returns The configured `DynamicModule`.
|
|
282
|
+
* @throws Error When the options fail validation.
|
|
283
|
+
* @example
|
|
284
|
+
* BymaxLoggerModule.forRoot({ service: { name: 'api', version: '1.0.0' } })
|
|
285
|
+
*/
|
|
286
|
+
static forRoot(options: typeof OPTIONS_TYPE): DynamicModule;
|
|
287
|
+
/**
|
|
288
|
+
* Asynchronous registration. Options are resolved lazily via the consumer's
|
|
289
|
+
* factory (`useFactory` + `inject` + `imports`, `useClass`, or `useExisting`);
|
|
290
|
+
* the Pino instance and the bootstrap log are produced only after they
|
|
291
|
+
* resolve, never at module-decoration time.
|
|
292
|
+
*
|
|
293
|
+
* Note: the destination-registry `onModuleInit` lifecycle hook is managed by
|
|
294
|
+
* `DestinationRegistry` itself, not by this factory. The destinations array is
|
|
295
|
+
* registered under `LOGGER_DESTINATIONS_TOKEN`; no built-in destination defines
|
|
296
|
+
* an `onInit` hook, so nothing is lost.
|
|
297
|
+
*
|
|
298
|
+
* HTTP access logging IS supported on this path: the global interceptor slot
|
|
299
|
+
* is always registered and gated at runtime against the resolved
|
|
300
|
+
* `http.isEnabled` (see {@link asyncHttpInterceptorProvider}). The catch-all
|
|
301
|
+
* exception FILTER is intentionally NOT auto-wired here — registering a global
|
|
302
|
+
* `@Catch()` filter from async config would either interfere with a consumer's
|
|
303
|
+
* own filters (when disabled) or require unsafe re-throwing. Async consumers who
|
|
304
|
+
* want it register `HttpExceptionFilter` themselves:
|
|
305
|
+
* `{ provide: APP_FILTER, useClass: HttpExceptionFilter }`.
|
|
306
|
+
*
|
|
307
|
+
* @param options - Async options (factory + inject + imports, or class).
|
|
308
|
+
* @returns The configured `DynamicModule`.
|
|
309
|
+
*/
|
|
310
|
+
static forRootAsync(options: typeof ASYNC_OPTIONS_TYPE): DynamicModule;
|
|
311
|
+
/**
|
|
312
|
+
* Replace NestJS's internal logger with this library's `PinoLoggerService` and
|
|
313
|
+
* flush any logs buffered since `NestFactory.create(..., { bufferLogs: true })`.
|
|
314
|
+
*
|
|
315
|
+
* Call once in `main.ts` AFTER creating the app and BEFORE `app.listen(...)`.
|
|
316
|
+
* The module cannot do this itself — a module never sees the
|
|
317
|
+
* `INestApplication` instance — so this static helper bridges the gap.
|
|
318
|
+
*
|
|
319
|
+
* @param app - The Nest application returned by `NestFactory.create()`.
|
|
320
|
+
* @throws Error When `BymaxLoggerModule` was not imported (no `PinoLoggerService`
|
|
321
|
+
* in the container) — a clear message instead of a cryptic DI failure. The
|
|
322
|
+
* original DI error is attached as the error `cause` so the underlying
|
|
323
|
+
* failure is preserved for debugging.
|
|
324
|
+
* @example
|
|
325
|
+
* const app = await NestFactory.create(AppModule, { bufferLogs: true })
|
|
326
|
+
* BymaxLoggerModule.useNestLogger(app)
|
|
327
|
+
* await app.listen(3000)
|
|
328
|
+
*/
|
|
329
|
+
static useNestLogger(app: INestApplication): void;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Pino-backed implementation of the NestJS `LoggerService` contract plus the
|
|
334
|
+
* structured `MODULE_ACTION_RESULT` API.
|
|
335
|
+
*
|
|
336
|
+
* For a per-class context label, inject a CHILD logger already bound to that
|
|
337
|
+
* context via `@InjectLogger(MyService.name)` — do NOT call `setContext()` per
|
|
338
|
+
* class: this is a singleton, so a per-class `setContext()` clobbers the label
|
|
339
|
+
* for every other holder (see {@link PinoLoggerService.setContext}).
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* constructor(
|
|
343
|
+
* @InjectLogger(UserService.name) private readonly logger: PinoLoggerService,
|
|
344
|
+
* ) {}
|
|
345
|
+
* // ...
|
|
346
|
+
* this.logger.info('USER_CREATED', 'New user registered', userId, { plan: 'pro' })
|
|
347
|
+
*/
|
|
348
|
+
declare class PinoLoggerService implements LoggerService, OnApplicationShutdown {
|
|
349
|
+
private readonly pino;
|
|
350
|
+
private context?;
|
|
351
|
+
constructor(pino: Logger);
|
|
352
|
+
/**
|
|
353
|
+
* NestJS `log` (info level). Last string parameter is treated as the context.
|
|
354
|
+
*
|
|
355
|
+
* @param message - The message to log.
|
|
356
|
+
* @param optionalParams - NestJS trailing params (context as last string).
|
|
357
|
+
*/
|
|
358
|
+
log(message: unknown, ...optionalParams: unknown[]): void;
|
|
359
|
+
/**
|
|
360
|
+
* NestJS `error`. When `message` is an `Error`, routes to the structured
|
|
361
|
+
* error path (serializing `err`). Otherwise follows the NestJS variadic
|
|
362
|
+
* contract `error(message, stack?, context?)`, reading the stack and context
|
|
363
|
+
* positionally so a trailing stack is never mistaken for the context.
|
|
364
|
+
*
|
|
365
|
+
* @param message - The message or `Error` to log.
|
|
366
|
+
* @param optionalParams - NestJS trailing params (`stack` at index 0, `context` at index 1).
|
|
367
|
+
*/
|
|
368
|
+
error(message: unknown, ...optionalParams: unknown[]): void;
|
|
369
|
+
/**
|
|
370
|
+
* NestJS `warn` (warn level).
|
|
371
|
+
*
|
|
372
|
+
* @param message - The message to log.
|
|
373
|
+
* @param optionalParams - NestJS trailing params (context as last string).
|
|
374
|
+
*/
|
|
375
|
+
warn(message: unknown, ...optionalParams: unknown[]): void;
|
|
376
|
+
/**
|
|
377
|
+
* NestJS `debug` (debug level).
|
|
378
|
+
*
|
|
379
|
+
* @param message - The message to log.
|
|
380
|
+
* @param optionalParams - NestJS trailing params (context as last string).
|
|
381
|
+
*/
|
|
382
|
+
debug(message: unknown, ...optionalParams: unknown[]): void;
|
|
383
|
+
/**
|
|
384
|
+
* NestJS `verbose` (mapped to Pino `trace`).
|
|
385
|
+
*
|
|
386
|
+
* @param message - The message to log.
|
|
387
|
+
* @param optionalParams - NestJS trailing params (context as last string).
|
|
388
|
+
*/
|
|
389
|
+
verbose(message: unknown, ...optionalParams: unknown[]): void;
|
|
390
|
+
/**
|
|
391
|
+
* NestJS `fatal` (fatal level). Declared non-optional here: Pino supports
|
|
392
|
+
* level 60 natively, and widening the optional `LoggerService.fatal?` to a
|
|
393
|
+
* required method stays type-compatible.
|
|
394
|
+
*
|
|
395
|
+
* @param message - The message to log.
|
|
396
|
+
* @param optionalParams - NestJS trailing params (context as last string).
|
|
397
|
+
*/
|
|
398
|
+
fatal(message: unknown, ...optionalParams: unknown[]): void;
|
|
399
|
+
/**
|
|
400
|
+
* Emit a structured info log.
|
|
401
|
+
*
|
|
402
|
+
* @param logKey - Convention key, e.g. `USER_CREATED`.
|
|
403
|
+
* @param message - Human-readable message.
|
|
404
|
+
* @param userId - Optional acting user identifier.
|
|
405
|
+
* @param metadata - Optional extra structured fields.
|
|
406
|
+
* @example
|
|
407
|
+
* logger.info('USER_CREATED', 'New user registered', userId, { plan: 'pro' })
|
|
408
|
+
*/
|
|
409
|
+
info(logKey: string, message: string, userId?: string, metadata?: Record<string, unknown>): void;
|
|
410
|
+
/**
|
|
411
|
+
* Emit a structured warning log.
|
|
412
|
+
*
|
|
413
|
+
* @param logKey - Convention key.
|
|
414
|
+
* @param message - Human-readable message.
|
|
415
|
+
* @param userId - Optional acting user identifier.
|
|
416
|
+
* @param metadata - Optional extra structured fields.
|
|
417
|
+
*/
|
|
418
|
+
warnStructured(logKey: string, message: string, userId?: string, metadata?: Record<string, unknown>): void;
|
|
419
|
+
/**
|
|
420
|
+
* Emit a structured error log with a serialized `err` object.
|
|
421
|
+
*
|
|
422
|
+
* @param logKey - Convention key.
|
|
423
|
+
* @param error - The error to serialize (`name` / `message` / `stack`).
|
|
424
|
+
* @param userId - Optional acting user identifier.
|
|
425
|
+
* @param metadata - Optional extra structured fields.
|
|
426
|
+
*/
|
|
427
|
+
errorStructured(logKey: string, error: Error, userId?: string, metadata?: Record<string, unknown>): void;
|
|
428
|
+
/**
|
|
429
|
+
* Set the global context label applied to subsequent logs from THIS instance.
|
|
430
|
+
*
|
|
431
|
+
* WARNING: `PinoLoggerService` is a singleton. Calling this per class clobbers
|
|
432
|
+
* the label for every other holder, and calling it at request time is a
|
|
433
|
+
* cross-request race. For a per-class label use `@InjectLogger(MyClass.name)`
|
|
434
|
+
* (which binds a child logger instead of mutating shared state); reserve
|
|
435
|
+
* `setContext()` for a single global / bootstrap label.
|
|
436
|
+
*
|
|
437
|
+
* @param context - The context label.
|
|
438
|
+
*/
|
|
439
|
+
setContext(context: string): void;
|
|
440
|
+
/**
|
|
441
|
+
* Access the underlying Pino instance for advanced use cases.
|
|
442
|
+
*
|
|
443
|
+
* @returns The wrapped Pino logger.
|
|
444
|
+
*/
|
|
445
|
+
getRawLogger(): Logger;
|
|
446
|
+
/**
|
|
447
|
+
* Create a child logger with additional permanent bindings, inheriting the
|
|
448
|
+
* current context label.
|
|
449
|
+
*
|
|
450
|
+
* @param bindings - Fields merged into every entry of the child logger.
|
|
451
|
+
* @returns A new `PinoLoggerService` wrapping the Pino child.
|
|
452
|
+
*/
|
|
453
|
+
child(bindings: Record<string, unknown>): PinoLoggerService;
|
|
454
|
+
/**
|
|
455
|
+
* NestJS shutdown hook. Destination flushing is handled by `DestinationRegistry`;
|
|
456
|
+
* this service has no additional teardown work.
|
|
457
|
+
*/
|
|
458
|
+
onApplicationShutdown(): void;
|
|
459
|
+
/**
|
|
460
|
+
* Serialize an Error into a plain, log-safe object matching Pino's built-in
|
|
461
|
+
* `err` serializer shape (`type`, `message`, `stack`).
|
|
462
|
+
*
|
|
463
|
+
* Reads `error.name` (NOT `error.constructor.name`) for `type`: native
|
|
464
|
+
* subclasses set `name` to their class name, and a sanitized plain-object error
|
|
465
|
+
* (from `sanitizeError`) carries the real class name on `name` — whereas its
|
|
466
|
+
* `constructor.name` would be the unhelpful `'Object'`, breaking `err.type`
|
|
467
|
+
* dashboards for every exception logged through `HttpExceptionFilter`.
|
|
468
|
+
*/
|
|
469
|
+
private serializeError;
|
|
470
|
+
/** Resolve the context: last string param wins, else the instance context. */
|
|
471
|
+
private resolveContext;
|
|
472
|
+
/** Emit a structured payload at info or warn level. */
|
|
473
|
+
private emitStructured;
|
|
474
|
+
/** Emit a NestJS-style variadic log, dispatching by level without computed indexing. */
|
|
475
|
+
private emitNestStyle;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Per-request context propagated via AsyncLocalStorage.
|
|
480
|
+
*
|
|
481
|
+
* Consumer code may extend with arbitrary keys via
|
|
482
|
+
* `LogContextService.set(key, value)`; the index signature permits the
|
|
483
|
+
* extension at the type level.
|
|
484
|
+
*/
|
|
485
|
+
interface LogContext$1 {
|
|
486
|
+
/** Correlation ID — typically from `x-request-id` header or generated. */
|
|
487
|
+
requestId?: string;
|
|
488
|
+
/** Multi-tenant identifier — read from `x-tenant-id` header or auth claim. */
|
|
489
|
+
tenantId?: string;
|
|
490
|
+
/** Authenticated user ID. */
|
|
491
|
+
userId?: string;
|
|
492
|
+
/** OpenTelemetry trace ID (32 hex chars). Injected by `TraceContextMixin`. */
|
|
493
|
+
traceId?: string;
|
|
494
|
+
/** OpenTelemetry span ID (16 hex chars). */
|
|
495
|
+
spanId?: string;
|
|
496
|
+
/** Free-form extensions added by application code. */
|
|
497
|
+
[key: string]: unknown;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Injectable wrapper around `AsyncLocalStorage<LogContext>`.
|
|
502
|
+
*
|
|
503
|
+
* A single instance is shared per module (NestJS singleton scope) so the
|
|
504
|
+
* code that opens a scope and the mixin that reads it operate on the same
|
|
505
|
+
* storage.
|
|
506
|
+
*
|
|
507
|
+
* @example
|
|
508
|
+
* logContext.run({ requestId: 'r_1' }, async () => {
|
|
509
|
+
* logContext.set('userId', 'u_1')
|
|
510
|
+
* await handler() // every log inside carries requestId + userId
|
|
511
|
+
* })
|
|
512
|
+
*/
|
|
513
|
+
declare class LogContextService {
|
|
514
|
+
private readonly als;
|
|
515
|
+
/**
|
|
516
|
+
* Run `callback` inside a fresh context scope. Every log emitted
|
|
517
|
+
* synchronously or asynchronously within the callback inherits `context`.
|
|
518
|
+
*
|
|
519
|
+
* @typeParam T - Return type of the callback.
|
|
520
|
+
* @param context - Initial context bag for the scope.
|
|
521
|
+
* @param callback - Function executed within the scope.
|
|
522
|
+
* @returns Whatever `callback` returns.
|
|
523
|
+
* @example
|
|
524
|
+
* logContext.run({ requestId: 'r_1', tenantId: 't_1' }, async () => {
|
|
525
|
+
* await userService.create(dto) // logs carry requestId + tenantId
|
|
526
|
+
* })
|
|
527
|
+
*/
|
|
528
|
+
run<T>(context: LogContext$1, callback: () => T): T;
|
|
529
|
+
/**
|
|
530
|
+
* Read the active context, or `undefined` when called outside any scope.
|
|
531
|
+
*
|
|
532
|
+
* @returns The active `LogContext`, or `undefined`.
|
|
533
|
+
*/
|
|
534
|
+
getStore(): LogContext$1 | undefined;
|
|
535
|
+
/**
|
|
536
|
+
* Add or override a single field on the active context.
|
|
537
|
+
*
|
|
538
|
+
* @param key - Field name to set.
|
|
539
|
+
* @param value - Field value.
|
|
540
|
+
* @throws Error When called outside a `run()` scope, or with a prototype-polluting key.
|
|
541
|
+
*/
|
|
542
|
+
set(key: string, value: unknown): void;
|
|
543
|
+
/**
|
|
544
|
+
* Read a single field from the active context.
|
|
545
|
+
*
|
|
546
|
+
* @typeParam T - Expected value type (caller-asserted).
|
|
547
|
+
* @param key - Field name to read.
|
|
548
|
+
* @returns The value cast to `T`, or `undefined` when unset / out of scope.
|
|
549
|
+
*/
|
|
550
|
+
get<T = unknown>(key: string): T | undefined;
|
|
551
|
+
/**
|
|
552
|
+
* Strip prototype-polluting keys from a caller-supplied context so an own
|
|
553
|
+
* `__proto__` / `constructor` / `prototype` key (e.g. from `JSON.parse` of an
|
|
554
|
+
* untrusted payload) can never reach the store — and, via the trace mixin's
|
|
555
|
+
* `Object.assign`, the emitted log entry. Mirrors the guard `set()` enforces,
|
|
556
|
+
* but drops the key rather than throwing: a logging concern must never crash
|
|
557
|
+
* the request that produced it.
|
|
558
|
+
*
|
|
559
|
+
* @param context - The raw context bag passed to `run()`.
|
|
560
|
+
* @returns A shallow copy with any forbidden keys removed.
|
|
561
|
+
*/
|
|
562
|
+
private sanitizeContext;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Canonical destination that writes log payloads to `process.stdout`.
|
|
567
|
+
*
|
|
568
|
+
* `process.stdout.write` is buffered by Node.js, which makes it safe to call
|
|
569
|
+
* on the hot path without blocking the event loop. The Pino producer is the
|
|
570
|
+
* back-pressure boundary — this destination is intentionally a thin sink.
|
|
571
|
+
*/
|
|
572
|
+
declare class DefaultStdoutDestination implements ILogDestination {
|
|
573
|
+
readonly name = "stdout-json";
|
|
574
|
+
readonly minLevel?: LogLevel;
|
|
575
|
+
/**
|
|
576
|
+
* @param opts.minLevel — Optional minimum level filter. Entries below this
|
|
577
|
+
* level are dropped by the destination registry before reaching `write`.
|
|
578
|
+
* Omit (or pass `undefined`) to accept every level.
|
|
579
|
+
*/
|
|
580
|
+
constructor(opts?: {
|
|
581
|
+
minLevel?: LogLevel;
|
|
582
|
+
});
|
|
583
|
+
/**
|
|
584
|
+
* Write a serialized JSON log entry (terminated with `\n`) to `stdout`.
|
|
585
|
+
*/
|
|
586
|
+
write(payload: string): void;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Pretty-print destination backed by `pino-pretty`.
|
|
591
|
+
*
|
|
592
|
+
* Lifecycle contract (driven by `DestinationRegistry`):
|
|
593
|
+
* 1. `onInit()` resolves `pino-pretty` and builds the transform (rendering to
|
|
594
|
+
* `process.stdout`).
|
|
595
|
+
* 2. `write()` forwards each serialized line to the transform.
|
|
596
|
+
* 3. `onShutdown()` flushes and closes the transform.
|
|
597
|
+
*
|
|
598
|
+
* @example
|
|
599
|
+
* BymaxLoggerModule.forRoot({
|
|
600
|
+
* service: { name: 'api', version: '1.0.0' },
|
|
601
|
+
* destinations: [new PrettyDevDestination()]
|
|
602
|
+
* })
|
|
603
|
+
*/
|
|
604
|
+
declare class PrettyDevDestination implements ILogDestination {
|
|
605
|
+
readonly name = "pretty-dev";
|
|
606
|
+
readonly minLevel?: LogLevel;
|
|
607
|
+
/**
|
|
608
|
+
* The `pino-pretty` transform stream. Undefined until {@link onInit} succeeds.
|
|
609
|
+
*/
|
|
610
|
+
private stream?;
|
|
611
|
+
/**
|
|
612
|
+
* Set when {@link onInit} fails (e.g. `pino-pretty` absent). Distinguishes a
|
|
613
|
+
* permanently-failed destination — whose writes are DROPPED, since the registry
|
|
614
|
+
* dropped it from the active set — from the transient pre-init window, where
|
|
615
|
+
* {@link write} falls back to raw stdout so a bootstrap log is not lost.
|
|
616
|
+
*/
|
|
617
|
+
private initFailed;
|
|
618
|
+
/**
|
|
619
|
+
* @param opts.minLevel — Optional minimum level filter applied by the
|
|
620
|
+
* destination registry's multi-stream wiring. Omit to accept every level.
|
|
621
|
+
*/
|
|
622
|
+
constructor(opts?: {
|
|
623
|
+
minLevel?: LogLevel;
|
|
624
|
+
});
|
|
625
|
+
/**
|
|
626
|
+
* Lazily resolve `pino-pretty` and build the colorized transform that renders
|
|
627
|
+
* each entry to `process.stdout`.
|
|
628
|
+
*
|
|
629
|
+
* @throws Error When `pino-pretty` is not installed — with an actionable
|
|
630
|
+
* message telling the consumer to install it or drop this destination.
|
|
631
|
+
*/
|
|
632
|
+
onInit(): Promise<void>;
|
|
633
|
+
/**
|
|
634
|
+
* Write a serialized NDJSON entry to the pretty transform.
|
|
635
|
+
*
|
|
636
|
+
* Three states:
|
|
637
|
+
* - initialized → forward to the pino-pretty transform;
|
|
638
|
+
* - init failed → DROP (the destination was removed from the active set, so
|
|
639
|
+
* it must not duplicate raw NDJSON to stdout);
|
|
640
|
+
* - pre-init (transient window before {@link onInit}, e.g. a bootstrap log)
|
|
641
|
+
* → fall back to raw stdout so the entry is not lost.
|
|
642
|
+
*
|
|
643
|
+
* @param payload — Newline-terminated JSON entry, UTF-8 encoded.
|
|
644
|
+
*/
|
|
645
|
+
write(payload: string): void;
|
|
646
|
+
/**
|
|
647
|
+
* Flush and close the pretty transform, resolving once the stream has ended.
|
|
648
|
+
* A no-op when {@link onInit} never ran.
|
|
649
|
+
*/
|
|
650
|
+
onShutdown(): Promise<void>;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Logs and renders any exception that escapes the request pipeline.
|
|
655
|
+
*/
|
|
656
|
+
declare class HttpExceptionFilter implements ExceptionFilter {
|
|
657
|
+
private readonly logger;
|
|
658
|
+
/**
|
|
659
|
+
* @param logger - The structured logger exception entries are written to.
|
|
660
|
+
* Injected by token explicitly: the package is bundled without
|
|
661
|
+
* `emitDecoratorMetadata`, so implicit class-type injection would not resolve.
|
|
662
|
+
*/
|
|
663
|
+
constructor(logger: PinoLoggerService);
|
|
664
|
+
/**
|
|
665
|
+
* Log the exception at the status-appropriate level and write the JSON
|
|
666
|
+
* response.
|
|
667
|
+
*
|
|
668
|
+
* @param exception - The thrown value (anything).
|
|
669
|
+
* @param host - The arguments host (switched to HTTP).
|
|
670
|
+
*/
|
|
671
|
+
catch(exception: unknown, host: ArgumentsHost): void;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Logs the full HTTP request lifecycle through a Pino-backed logger.
|
|
676
|
+
*/
|
|
677
|
+
declare class HttpLoggingInterceptor implements NestInterceptor {
|
|
678
|
+
private readonly logger;
|
|
679
|
+
/** Path patterns that bypass HTTP logging entirely (health checks, metrics). */
|
|
680
|
+
private readonly excludePaths;
|
|
681
|
+
/**
|
|
682
|
+
* @param logger - The structured logger the lifecycle entries are written to.
|
|
683
|
+
* Injected by token explicitly: the package is bundled without
|
|
684
|
+
* `emitDecoratorMetadata`, so implicit class-type injection would not resolve.
|
|
685
|
+
* @param options - Resolved module options (read for `http.excludePaths`).
|
|
686
|
+
* Injected by token for the same bundling reason.
|
|
687
|
+
*/
|
|
688
|
+
constructor(logger: PinoLoggerService, options: ResolvedBymaxLoggerModuleOptions);
|
|
689
|
+
/**
|
|
690
|
+
* Whether a request path bypasses HTTP logging (matches any `excludePaths`).
|
|
691
|
+
*
|
|
692
|
+
* @param path - The query-stripped request path.
|
|
693
|
+
* @returns `true` when the path matches a configured exclude pattern.
|
|
694
|
+
*/
|
|
695
|
+
private isExcluded;
|
|
696
|
+
/**
|
|
697
|
+
* Emit the START entry, then attach success/redirect/error logging to the
|
|
698
|
+
* downstream observable while always re-propagating any thrown exception.
|
|
699
|
+
*
|
|
700
|
+
* @param context - The current execution context (HTTP).
|
|
701
|
+
* @param next - The downstream call handler.
|
|
702
|
+
* @returns The (unmodified) response stream.
|
|
703
|
+
*/
|
|
704
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
|
|
705
|
+
/**
|
|
706
|
+
* Log a non-throwing completion: SUCCESS for 2xx, REDIRECT for 3xx, nothing
|
|
707
|
+
* for any other status (those are surfaced via the error path instead).
|
|
708
|
+
*
|
|
709
|
+
* @param statusCode - The final response status code.
|
|
710
|
+
* @param method - The HTTP method.
|
|
711
|
+
* @param url - The normalized request URL.
|
|
712
|
+
* @param userId - The acting user id, when known.
|
|
713
|
+
* @param duration - The elapsed time in milliseconds.
|
|
714
|
+
*/
|
|
715
|
+
private logSuccess;
|
|
716
|
+
/**
|
|
717
|
+
* Log a thrown completion: SERVER_ERROR (error level, with stack) for 5xx,
|
|
718
|
+
* CLIENT_ERROR (warn level) for an `HttpException` below 500. A non-`HttpException`
|
|
719
|
+
* always maps to 500, so it is reported as a server error.
|
|
720
|
+
*
|
|
721
|
+
* @param err - The thrown value (anything).
|
|
722
|
+
* @param method - The HTTP method.
|
|
723
|
+
* @param url - The normalized request URL.
|
|
724
|
+
* @param userId - The acting user id, when known.
|
|
725
|
+
* @param duration - The elapsed time in milliseconds.
|
|
726
|
+
*/
|
|
727
|
+
private logError;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* `applyRequestIdMiddleware` consumer helper.
|
|
732
|
+
*
|
|
733
|
+
* Layer: server/middlewares — NestJS requires middleware to be wired in a
|
|
734
|
+
* module's `configure(consumer)` hook, which the library cannot do on the
|
|
735
|
+
* consumer's behalf. This helper encapsulates the one-liner so consumers do not
|
|
736
|
+
* import the middleware class directly.
|
|
737
|
+
*/
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Register {@link RequestIdMiddleware} on the given consumer.
|
|
741
|
+
*
|
|
742
|
+
* @param consumer - The `MiddlewareConsumer` from the module's `configure` hook.
|
|
743
|
+
* @param routes - Route pattern(s) to apply the middleware to. Defaults to all.
|
|
744
|
+
* @example
|
|
745
|
+
* import { applyRequestIdMiddleware } from '@bymax-one/nest-logger'
|
|
746
|
+
*
|
|
747
|
+
* @Module({ imports: [BymaxLoggerModule.forRoot({ service, http: { isEnabled: true } })] })
|
|
748
|
+
* export class AppModule implements NestModule {
|
|
749
|
+
* configure(consumer: MiddlewareConsumer): void {
|
|
750
|
+
* applyRequestIdMiddleware(consumer)
|
|
751
|
+
* }
|
|
752
|
+
* }
|
|
753
|
+
*/
|
|
754
|
+
declare function applyRequestIdMiddleware(consumer: MiddlewareConsumer, routes?: string): void;
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Reads (or generates) the correlation id and starts the per-request log
|
|
758
|
+
* context scope.
|
|
759
|
+
*
|
|
760
|
+
* @example
|
|
761
|
+
* // Wire it in the consumer's AppModule:
|
|
762
|
+
* export class AppModule implements NestModule {
|
|
763
|
+
* configure(consumer: MiddlewareConsumer): void {
|
|
764
|
+
* applyRequestIdMiddleware(consumer)
|
|
765
|
+
* }
|
|
766
|
+
* }
|
|
767
|
+
*/
|
|
768
|
+
declare class RequestIdMiddleware implements NestMiddleware {
|
|
769
|
+
private readonly logContext;
|
|
770
|
+
/** Header name carrying the tenant id, resolved from module options. */
|
|
771
|
+
private readonly tenantIdHeader;
|
|
772
|
+
/** Whether to mint a `requestId` when the inbound header is absent. */
|
|
773
|
+
private readonly shouldGenerateRequestId;
|
|
774
|
+
/**
|
|
775
|
+
* @param logContext - The AsyncLocalStorage-backed context service. Injected by
|
|
776
|
+
* token explicitly: the package is bundled without `emitDecoratorMetadata`,
|
|
777
|
+
* so implicit class-type injection would not resolve.
|
|
778
|
+
* @param options - Resolved module options (read for `http.tenantIdHeader` and
|
|
779
|
+
* `http.shouldGenerateRequestId`).
|
|
780
|
+
*/
|
|
781
|
+
constructor(logContext: LogContextService, options: ResolvedBymaxLoggerModuleOptions);
|
|
782
|
+
/**
|
|
783
|
+
* Echo or mint the `x-request-id`, expose it on the response, and run the
|
|
784
|
+
* remainder of the request inside a fresh log-context scope.
|
|
785
|
+
*
|
|
786
|
+
* @param req - The incoming Express request.
|
|
787
|
+
* @param res - The outgoing Express response.
|
|
788
|
+
* @param next - The next handler in the chain.
|
|
789
|
+
*/
|
|
790
|
+
use(req: Request, res: Response, next: NextFunction): void;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* Inject a logger into a constructor parameter.
|
|
795
|
+
*
|
|
796
|
+
* With a `context` the injected logger is a child bound to that context (every
|
|
797
|
+
* log carries it); without one, the shared `PinoLoggerService` is injected.
|
|
798
|
+
*
|
|
799
|
+
* @param context - Optional context label (typically the host class name).
|
|
800
|
+
* @returns A parameter decorator wiring the injection (and recording the label).
|
|
801
|
+
* @example
|
|
802
|
+
* @Injectable()
|
|
803
|
+
* export class UsersService {
|
|
804
|
+
* constructor(@InjectLogger('UsersService') private readonly logger: PinoLoggerService) {}
|
|
805
|
+
* }
|
|
806
|
+
* // every `this.logger.*` entry now carries context: 'UsersService'
|
|
807
|
+
*/
|
|
808
|
+
declare function InjectLogger(context?: string): ParameterDecorator;
|
|
809
|
+
|
|
810
|
+
/** Reflect-metadata key under which the class context label is stored. */
|
|
811
|
+
declare const LOG_CONTEXT_METADATA_KEY = "bymax_logger:log_context";
|
|
812
|
+
/**
|
|
813
|
+
* Set the log-context label for a class. All loggers injected within the class
|
|
814
|
+
* are intended to adopt this label (auto-application requires a wired interceptor).
|
|
815
|
+
*
|
|
816
|
+
* @param name - The context label (typically the class name).
|
|
817
|
+
* @returns A NestJS metadata decorator carrying the label.
|
|
818
|
+
* @example
|
|
819
|
+
* @LogContext('PaymentsService')
|
|
820
|
+
* @Injectable()
|
|
821
|
+
* export class PaymentsService {}
|
|
822
|
+
*/
|
|
823
|
+
declare const LogContext: (name: string) => CustomDecorator<string>;
|
|
824
|
+
|
|
825
|
+
/**
|
|
826
|
+
* Measure and log a method's execution time.
|
|
827
|
+
*
|
|
828
|
+
* @param thresholdMs - Duration (ms) above which the call is logged as slow.
|
|
829
|
+
* Defaults to {@link DEFAULT_THRESHOLD_MS} (1000).
|
|
830
|
+
* @returns A method decorator that wraps the original implementation.
|
|
831
|
+
* @remarks The wrapped method always returns a `Promise` (see the file header):
|
|
832
|
+
* apply only to async methods or callers that can await the result.
|
|
833
|
+
* @example
|
|
834
|
+
* class ReportService {
|
|
835
|
+
* constructor(@InjectLogger() private readonly logger: PinoLoggerService) {}
|
|
836
|
+
*
|
|
837
|
+
* @LogPerformance(500) // warn if it takes > 500ms
|
|
838
|
+
* async generateReport(): Promise<void> {}
|
|
839
|
+
* }
|
|
840
|
+
*/
|
|
841
|
+
declare function LogPerformance(thresholdMs?: number): MethodDecorator;
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* Dependency-injection tokens used by `BymaxLoggerModule`.
|
|
845
|
+
*
|
|
846
|
+
* Symbols (rather than strings) are used to guarantee uniqueness across the
|
|
847
|
+
* entire NestJS DI graph — two consumers cannot collide on the same token by
|
|
848
|
+
* accident, and accidental string typos cannot resolve a foreign provider.
|
|
849
|
+
*
|
|
850
|
+
* This mirrors the pattern established by `@bymax-one/nest-auth` for every
|
|
851
|
+
* shared Bymax NestJS library.
|
|
852
|
+
*/
|
|
853
|
+
/** Injection token for the resolved `BymaxLoggerModuleOptions` snapshot. */
|
|
854
|
+
declare const LOGGER_OPTIONS_TOKEN: unique symbol;
|
|
855
|
+
/** Injection token for the underlying Pino instance (used by services and mixins). */
|
|
856
|
+
declare const LOGGER_PINO_INSTANCE_TOKEN: unique symbol;
|
|
857
|
+
/** Injection token for the resolved destinations array (read-only at runtime). */
|
|
858
|
+
declare const LOGGER_DESTINATIONS_TOKEN: unique symbol;
|
|
859
|
+
/** Injection token for the per-request AsyncLocalStorage-backed log context. */
|
|
860
|
+
declare const LOG_CONTEXT_TOKEN: unique symbol;
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* Canonical list of redact paths merged into Pino's `redact.paths` option by
|
|
864
|
+
* default. Expected length: 27 common fields × 4 depths + 5 absolute = **113**.
|
|
865
|
+
*/
|
|
866
|
+
declare const DEFAULT_REDACT_PATHS: readonly string[];
|
|
867
|
+
|
|
868
|
+
/**
|
|
869
|
+
* Shape of a single log entry as serialized to JSON output.
|
|
870
|
+
*
|
|
871
|
+
* Used by destinations and downstream parsers (Datadog, Loki, Elastic).
|
|
872
|
+
* The trailing index signature `[key: string]: unknown` covers arbitrary
|
|
873
|
+
* structured fields injected by application logs.
|
|
874
|
+
*/
|
|
875
|
+
interface LogEntry {
|
|
876
|
+
/** Pino numeric level (30 = info, 50 = error, etc.). */
|
|
877
|
+
level: number;
|
|
878
|
+
/** ISO 8601 UTC timestamp string OR epoch milliseconds (numeric). */
|
|
879
|
+
time: string | number;
|
|
880
|
+
/** Human-readable message. */
|
|
881
|
+
msg: string;
|
|
882
|
+
/** Convention key — `MODULE_ACTION_RESULT` format. */
|
|
883
|
+
logKey?: string;
|
|
884
|
+
/** Service metadata snapshot — see {@link ServiceMetadata}. */
|
|
885
|
+
service?: ServiceMetadata;
|
|
886
|
+
/** Optional NestJS context (typically the emitting class name). */
|
|
887
|
+
context?: string;
|
|
888
|
+
/** Optional correlation IDs propagated via AsyncLocalStorage. */
|
|
889
|
+
requestId?: string;
|
|
890
|
+
tenantId?: string;
|
|
891
|
+
userId?: string;
|
|
892
|
+
/** Optional trace context injected when OpenTelemetry SDK is active. */
|
|
893
|
+
traceId?: string;
|
|
894
|
+
spanId?: string;
|
|
895
|
+
/** Arbitrary additional fields produced by application logs. */
|
|
896
|
+
[key: string]: unknown;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/**
|
|
900
|
+
* Regex enforcing the canonical Bymax log-key convention.
|
|
901
|
+
*
|
|
902
|
+
* The convention is `MODULE_ACTION_RESULT` (e.g., `USER_CREATED`,
|
|
903
|
+
* `AUTH_LOGIN_SUCCESS`), but the regex accepts **two or more** uppercase words —
|
|
904
|
+
* keys in {@link RESERVED_LOG_KEYS} legitimately use four words
|
|
905
|
+
* (e.g. `HTTP_REQUEST_CLIENT_ERROR`, `LOGGER_DESTINATION_WRITE_FAILED`).
|
|
906
|
+
*
|
|
907
|
+
* Pattern: two mandatory uppercase-prefixed groups separated by `_`, plus one
|
|
908
|
+
* optional third group. Within each group, `[A-Z0-9_]+` allows digits and
|
|
909
|
+
* underscores after the leading uppercase letter, which enables additional
|
|
910
|
+
* semantic words inside a group (e.g., `HTTP_REQUEST_CLIENT` is the first group
|
|
911
|
+
* in `HTTP_REQUEST_CLIENT_ERROR`). As a side-effect, strings with a double
|
|
912
|
+
* underscore (`HELLO__WORLD`) also pass; this is an accepted edge case.
|
|
913
|
+
*
|
|
914
|
+
* Enforcement rules:
|
|
915
|
+
* - At least 2 uppercase-prefixed words (`[A-Z][A-Z0-9_]+`)
|
|
916
|
+
* - Each pattern group starts with `A-Z`; no lowercase letters permitted
|
|
917
|
+
* - No leading or trailing underscores at the top level
|
|
918
|
+
*
|
|
919
|
+
* @example
|
|
920
|
+
* LOG_KEYS_CONVENTION_REGEX.test('USER_CREATED') // true
|
|
921
|
+
* LOG_KEYS_CONVENTION_REGEX.test('AUTH_LOGIN_SUCCESS') // true
|
|
922
|
+
* LOG_KEYS_CONVENTION_REGEX.test('HTTP_REQUEST_CLIENT_ERROR') // true (4 words)
|
|
923
|
+
* LOG_KEYS_CONVENTION_REGEX.test('login_success') // false (lowercase)
|
|
924
|
+
* LOG_KEYS_CONVENTION_REGEX.test('LOGIN') // false (single word)
|
|
925
|
+
*/
|
|
926
|
+
declare const LOG_KEYS_CONVENTION_REGEX: RegExp;
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* Log keys reserved by `@bymax-one/nest-logger` for its own structured events.
|
|
930
|
+
*
|
|
931
|
+
* Consumer apps SHOULD NOT use these names for application-level events
|
|
932
|
+
* to avoid collision in log aggregation queries (Datadog, Loki, Elastic).
|
|
933
|
+
*/
|
|
934
|
+
declare const RESERVED_LOG_KEYS: Readonly<{
|
|
935
|
+
readonly LOGGER_BOOTSTRAP_OK: "LOGGER_BOOTSTRAP_OK";
|
|
936
|
+
readonly LOGGER_BOOTSTRAP_WARNING: "LOGGER_BOOTSTRAP_WARNING";
|
|
937
|
+
readonly LOGGER_SHUTDOWN_OK: "LOGGER_SHUTDOWN_OK";
|
|
938
|
+
readonly HTTP_REQUEST_START: "HTTP_REQUEST_START";
|
|
939
|
+
readonly HTTP_REQUEST_SUCCESS: "HTTP_REQUEST_SUCCESS";
|
|
940
|
+
readonly HTTP_REQUEST_REDIRECT: "HTTP_REQUEST_REDIRECT";
|
|
941
|
+
readonly HTTP_REQUEST_CLIENT_ERROR: "HTTP_REQUEST_CLIENT_ERROR";
|
|
942
|
+
readonly HTTP_REQUEST_SERVER_ERROR: "HTTP_REQUEST_SERVER_ERROR";
|
|
943
|
+
readonly HTTP_REQUEST_COMPLETED: "HTTP_REQUEST_COMPLETED";
|
|
944
|
+
readonly HTTP_EXCEPTION_HANDLED: "HTTP_EXCEPTION_HANDLED";
|
|
945
|
+
readonly HTTP_EXCEPTION_UNHANDLED: "HTTP_EXCEPTION_UNHANDLED";
|
|
946
|
+
readonly METHOD_EXECUTION: "METHOD_EXECUTION";
|
|
947
|
+
readonly METHOD_SLOW_EXECUTION: "METHOD_SLOW_EXECUTION";
|
|
948
|
+
readonly LOGGER_DESTINATION_INIT_FAILED: "LOGGER_DESTINATION_INIT_FAILED";
|
|
949
|
+
readonly LOGGER_DESTINATION_WRITE_FAILED: "LOGGER_DESTINATION_WRITE_FAILED";
|
|
950
|
+
readonly LOGGER_ENTRY_TRUNCATED: "LOGGER_ENTRY_TRUNCATED";
|
|
951
|
+
}>;
|
|
952
|
+
|
|
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 };
|