@bymax-one/nest-logger 1.4.1 → 1.4.3
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 +173 -5
- package/README.md +183 -81
- package/dist/server/index.cjs +159 -6
- package/dist/server/index.d.cts +75 -22
- package/dist/server/index.d.ts +75 -22
- package/dist/server/index.mjs +157 -7
- package/dist/shared/index.d.cts +2 -1
- package/dist/shared/index.d.ts +2 -1
- package/package.json +2 -2
package/dist/server/index.cjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var common = require('@nestjs/common');
|
|
4
4
|
var core = require('@nestjs/core');
|
|
5
|
+
var util = require('util');
|
|
5
6
|
var rxjs = require('rxjs');
|
|
6
7
|
var pino = require('pino');
|
|
7
8
|
var module$1 = require('module');
|
|
@@ -29,7 +30,7 @@ var DEFAULT_HTTP = {
|
|
|
29
30
|
isEnabled: false,
|
|
30
31
|
shouldCaptureExceptions: true,
|
|
31
32
|
shouldGenerateRequestId: true,
|
|
32
|
-
excludePaths: [/^\/health$/, /^\/metrics$/],
|
|
33
|
+
excludePaths: [/^\/health$/, /^\/health\/live$/, /^\/health\/ready$/, /^\/metrics$/],
|
|
33
34
|
tenantIdHeader: "x-tenant-id"
|
|
34
35
|
};
|
|
35
36
|
var DEFAULT_OTEL = {
|
|
@@ -114,7 +115,36 @@ var LOG_LEVEL_PRIORITY = Object.freeze([
|
|
|
114
115
|
]);
|
|
115
116
|
|
|
116
117
|
// src/server/config/validate-options.ts
|
|
117
|
-
function
|
|
118
|
+
function reject(path, rule, got) {
|
|
119
|
+
const received = got === void 0 ? "" : `. Got: ${describeValue(got)}`;
|
|
120
|
+
throw new Error(`[BymaxLoggerModule] options.${path} ${rule}${received}`);
|
|
121
|
+
}
|
|
122
|
+
function describeValue(value) {
|
|
123
|
+
if (value === null) return "null";
|
|
124
|
+
if (Array.isArray(value)) return "an array";
|
|
125
|
+
if (typeof value === "object") return "an object";
|
|
126
|
+
if (typeof value === "function") return "a function";
|
|
127
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
128
|
+
return String(value);
|
|
129
|
+
}
|
|
130
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
131
|
+
var isNonEmptyString = (value) => typeof value === "string" && value.length > 0;
|
|
132
|
+
function checkBoolean(path, value) {
|
|
133
|
+
if (value !== void 0 && typeof value !== "boolean") {
|
|
134
|
+
reject(path, "must be a boolean", value);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function checkNonEmptyString(path, value) {
|
|
138
|
+
if (value !== void 0 && !isNonEmptyString(value)) {
|
|
139
|
+
reject(path, "must be a non-empty string", value);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function checkGroup(path, value) {
|
|
143
|
+
if (value === void 0) return void 0;
|
|
144
|
+
if (!isRecord(value)) reject(path, "must be an object", value);
|
|
145
|
+
return value;
|
|
146
|
+
}
|
|
147
|
+
function validateCore(options) {
|
|
118
148
|
if (!options.service) {
|
|
119
149
|
throw new Error("[BymaxLoggerModule] options.service is required");
|
|
120
150
|
}
|
|
@@ -131,9 +161,15 @@ function validateOptions(options) {
|
|
|
131
161
|
)}. Got: ${String(options.level)}`
|
|
132
162
|
);
|
|
133
163
|
}
|
|
164
|
+
const size = options.maxEntrySizeBytes;
|
|
165
|
+
if (size !== void 0 && !Number.isFinite(size)) {
|
|
166
|
+
reject("maxEntrySizeBytes", "must be a finite number", size);
|
|
167
|
+
}
|
|
134
168
|
if (options.maxEntrySizeBytes !== void 0 && options.maxEntrySizeBytes <= 0) {
|
|
135
169
|
throw new Error("[BymaxLoggerModule] options.maxEntrySizeBytes must be > 0");
|
|
136
170
|
}
|
|
171
|
+
}
|
|
172
|
+
function validateClosedSets(options) {
|
|
137
173
|
if (options.redactStrategy !== void 0 && options.redactStrategy !== "names" && options.redactStrategy !== "paths") {
|
|
138
174
|
throw new Error(
|
|
139
175
|
`[BymaxLoggerModule] options.redactStrategy must be 'names' or 'paths'. Got: ${String(
|
|
@@ -163,6 +199,109 @@ function validateOptions(options) {
|
|
|
163
199
|
);
|
|
164
200
|
}
|
|
165
201
|
}
|
|
202
|
+
function validateCustomization(options) {
|
|
203
|
+
checkBoolean("isGlobal", options.isGlobal);
|
|
204
|
+
checkBoolean("shouldDisableDefaultRedact", options.shouldDisableDefaultRedact);
|
|
205
|
+
if (options.redactCensor !== void 0 && typeof options.redactCensor !== "string") {
|
|
206
|
+
reject("redactCensor", "must be a string", options.redactCensor);
|
|
207
|
+
}
|
|
208
|
+
if (options.timestamp !== void 0 && typeof options.timestamp !== "function") {
|
|
209
|
+
reject("timestamp", "must be a function", options.timestamp);
|
|
210
|
+
}
|
|
211
|
+
const paths = options.redactPaths;
|
|
212
|
+
if (paths !== void 0) {
|
|
213
|
+
if (!Array.isArray(paths)) reject("redactPaths", "must be an array of strings", paths);
|
|
214
|
+
paths.forEach((entry, index) => {
|
|
215
|
+
if (!isNonEmptyString(entry)) {
|
|
216
|
+
reject(`redactPaths[${String(index)}]`, "must be a non-empty string", entry);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
const serializers = options.serializers;
|
|
221
|
+
if (serializers !== void 0) {
|
|
222
|
+
if (!isRecord(serializers)) reject("serializers", "must be an object", serializers);
|
|
223
|
+
for (const [key, serializer] of Object.entries(serializers)) {
|
|
224
|
+
if (typeof serializer !== "function") {
|
|
225
|
+
reject(`serializers.${key}`, "must be a function", serializer);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
var UNREADABLE = /* @__PURE__ */ Symbol("unreadable");
|
|
231
|
+
function readMember(destination, key) {
|
|
232
|
+
try {
|
|
233
|
+
return Reflect.get(destination, key);
|
|
234
|
+
} catch {
|
|
235
|
+
return UNREADABLE;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function validateDestination(entry, index) {
|
|
239
|
+
const path = `destinations[${String(index)}]`;
|
|
240
|
+
if (!isRecord(entry)) {
|
|
241
|
+
reject(path, "must be an object implementing ILogDestination", entry);
|
|
242
|
+
}
|
|
243
|
+
const write = readMember(entry, "write");
|
|
244
|
+
if (write !== UNREADABLE && typeof write !== "function") {
|
|
245
|
+
reject(path, "must have a write(payload) method");
|
|
246
|
+
}
|
|
247
|
+
const name = readMember(entry, "name");
|
|
248
|
+
if (name !== UNREADABLE && !isNonEmptyString(name)) {
|
|
249
|
+
reject(path, "must have a non-empty string name", name);
|
|
250
|
+
}
|
|
251
|
+
for (const hook of ["onInit", "onShutdown", "onRegistryReady"]) {
|
|
252
|
+
const value = readMember(entry, hook);
|
|
253
|
+
if (value !== UNREADABLE && value !== void 0 && typeof value !== "function") {
|
|
254
|
+
reject(`${path}.${hook}`, "must be a function", value);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
function validateDestinations(options) {
|
|
259
|
+
const destinations = options.destinations;
|
|
260
|
+
if (destinations === void 0) return;
|
|
261
|
+
if (!Array.isArray(destinations)) {
|
|
262
|
+
reject("destinations", "must be an array of ILogDestination", destinations);
|
|
263
|
+
}
|
|
264
|
+
destinations.forEach((entry, index) => validateDestination(entry, index));
|
|
265
|
+
}
|
|
266
|
+
function validateHttp(options) {
|
|
267
|
+
const http = checkGroup("http", options.http);
|
|
268
|
+
if (http === void 0) return;
|
|
269
|
+
checkBoolean("http.isEnabled", http["isEnabled"]);
|
|
270
|
+
checkBoolean("http.shouldCaptureExceptions", http["shouldCaptureExceptions"]);
|
|
271
|
+
checkBoolean("http.shouldGenerateRequestId", http["shouldGenerateRequestId"]);
|
|
272
|
+
const excludePaths = http["excludePaths"];
|
|
273
|
+
if (excludePaths !== void 0) {
|
|
274
|
+
if (!Array.isArray(excludePaths)) {
|
|
275
|
+
reject("http.excludePaths", "must be an array of RegExp", excludePaths);
|
|
276
|
+
}
|
|
277
|
+
excludePaths.forEach((pattern, index) => {
|
|
278
|
+
if (!util.types.isRegExp(pattern)) {
|
|
279
|
+
reject(`http.excludePaths[${String(index)}]`, "must be a RegExp", pattern);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
checkNonEmptyString("http.tenantIdHeader", http["tenantIdHeader"]);
|
|
284
|
+
}
|
|
285
|
+
function validateOtel(options) {
|
|
286
|
+
const otel = checkGroup("otel", options.otel);
|
|
287
|
+
if (otel === void 0) return;
|
|
288
|
+
checkBoolean("otel.shouldAutoInjectTraceContext", otel["shouldAutoInjectTraceContext"]);
|
|
289
|
+
const format = otel["fieldFormat"];
|
|
290
|
+
if (format !== void 0 && format !== "camelCase" && format !== "snake_case") {
|
|
291
|
+
reject("otel.fieldFormat", "must be 'camelCase' or 'snake_case'", format);
|
|
292
|
+
}
|
|
293
|
+
checkNonEmptyString("otel.traceIdField", otel["traceIdField"]);
|
|
294
|
+
checkNonEmptyString("otel.spanIdField", otel["spanIdField"]);
|
|
295
|
+
checkNonEmptyString("otel.traceFlagsField", otel["traceFlagsField"]);
|
|
296
|
+
}
|
|
297
|
+
function validateOptions(options) {
|
|
298
|
+
validateCore(options);
|
|
299
|
+
validateClosedSets(options);
|
|
300
|
+
validateCustomization(options);
|
|
301
|
+
validateDestinations(options);
|
|
302
|
+
validateHttp(options);
|
|
303
|
+
validateOtel(options);
|
|
304
|
+
}
|
|
166
305
|
|
|
167
306
|
// src/server/constants/injection-tokens.constants.ts
|
|
168
307
|
var LOGGER_OPTIONS_TOKEN = /* @__PURE__ */ Symbol("BYMAX_LOGGER_OPTIONS");
|
|
@@ -1609,8 +1748,11 @@ function buildPinoInstance(options, logContext, destinations, health) {
|
|
|
1609
1748
|
options.resourceFormat,
|
|
1610
1749
|
extraServiceFields(options.service)
|
|
1611
1750
|
),
|
|
1612
|
-
// Pino requires the timestamp fn to emit the `,"time"
|
|
1613
|
-
|
|
1751
|
+
// Pino requires the timestamp fn to emit the `,"time":<json>` fragment, and
|
|
1752
|
+
// inserts it into the line verbatim. The value comes from consumer code, so it
|
|
1753
|
+
// is encoded as a JSON string here: a quote, a backslash or a newline in it must
|
|
1754
|
+
// neither break the line nor add fields to it.
|
|
1755
|
+
timestamp: () => `,"time":${JSON.stringify(String(options.timestamp()))}`,
|
|
1614
1756
|
formatters: {
|
|
1615
1757
|
// Emit the level as a string label instead of the numeric code — easier
|
|
1616
1758
|
// for log aggregators. Trace context is injected via `mixin`, never via
|
|
@@ -2468,10 +2610,18 @@ var PrettyDevDestination = class {
|
|
|
2468
2610
|
* Lazily resolve `pino-pretty` and build the colorized transform that renders
|
|
2469
2611
|
* each entry to `process.stdout`.
|
|
2470
2612
|
*
|
|
2613
|
+
* @throws Error When `NODE_ENV` is `production`: this sink is development
|
|
2614
|
+
* output, so it refuses rather than put ANSI colour into the log stream.
|
|
2471
2615
|
* @throws Error When `pino-pretty` is not installed — with an actionable
|
|
2472
2616
|
* message telling the consumer to install it or drop this destination.
|
|
2473
2617
|
*/
|
|
2474
2618
|
async onInit() {
|
|
2619
|
+
if (process.env["NODE_ENV"] === "production") {
|
|
2620
|
+
this.initFailed = true;
|
|
2621
|
+
throw new Error(
|
|
2622
|
+
"[PrettyDevDestination] refused in production (NODE_ENV=production): it renders ANSI colour through a main-thread transform. Use DefaultStdoutDestination, or register PrettyDevDestination only outside production."
|
|
2623
|
+
);
|
|
2624
|
+
}
|
|
2475
2625
|
try {
|
|
2476
2626
|
const { build } = await import('pino-pretty');
|
|
2477
2627
|
this.stream = build({
|
|
@@ -2598,8 +2748,8 @@ var PrettyDevDestination = class {
|
|
|
2598
2748
|
this.flushBuffer(writeRawToStdout);
|
|
2599
2749
|
return;
|
|
2600
2750
|
}
|
|
2601
|
-
await new Promise((resolve,
|
|
2602
|
-
stream.once("error",
|
|
2751
|
+
await new Promise((resolve, reject2) => {
|
|
2752
|
+
stream.once("error", reject2);
|
|
2603
2753
|
stream.end(resolve);
|
|
2604
2754
|
});
|
|
2605
2755
|
}
|
|
@@ -3033,6 +3183,7 @@ exports.InjectLogger = InjectLogger;
|
|
|
3033
3183
|
exports.LOGGER_DESTINATIONS_TOKEN = LOGGER_DESTINATIONS_TOKEN;
|
|
3034
3184
|
exports.LOGGER_OPTIONS_TOKEN = LOGGER_OPTIONS_TOKEN;
|
|
3035
3185
|
exports.LOGGER_PINO_INSTANCE_TOKEN = LOGGER_PINO_INSTANCE_TOKEN;
|
|
3186
|
+
exports.LOGGER_REDACTOR_TOKEN = LOGGER_REDACTOR_TOKEN;
|
|
3036
3187
|
exports.LOG_CONTEXT_METADATA_KEY = LOG_CONTEXT_METADATA_KEY;
|
|
3037
3188
|
exports.LOG_CONTEXT_TOKEN = LOG_CONTEXT_TOKEN;
|
|
3038
3189
|
exports.LOG_KEYS_CONVENTION_REGEX = LOG_KEYS_CONVENTION_REGEX;
|
|
@@ -3041,7 +3192,9 @@ exports.LogPerformance = LogPerformance;
|
|
|
3041
3192
|
exports.PINO_LEVEL_NAMES = PINO_LEVEL_NAMES;
|
|
3042
3193
|
exports.PINO_LEVEL_NUMBERS = PINO_LEVEL_NUMBERS;
|
|
3043
3194
|
exports.PrettyDevDestination = PrettyDevDestination;
|
|
3195
|
+
exports.REDACT_COMMON_FIELDS = REDACT_COMMON_FIELDS;
|
|
3044
3196
|
exports.RESERVED_LOG_KEYS = RESERVED_LOG_KEYS;
|
|
3045
3197
|
exports.RESERVED_LOG_KEYS_NOT_EMITTED = RESERVED_LOG_KEYS_NOT_EMITTED;
|
|
3046
3198
|
exports.applyAccessLog = applyAccessLog;
|
|
3047
3199
|
exports.applyRequestIdMiddleware = applyRequestIdMiddleware;
|
|
3200
|
+
exports.isValidTraceId = isValidTraceId;
|
package/dist/server/index.d.cts
CHANGED
|
@@ -52,7 +52,9 @@ interface ILogDestination {
|
|
|
52
52
|
readonly name: string;
|
|
53
53
|
/**
|
|
54
54
|
* Minimum log level this destination accepts. Entries below this level
|
|
55
|
-
* are not written.
|
|
55
|
+
* are not written. The effective level is the STRICTER of this and the module
|
|
56
|
+
* `level`, so it can only narrow what the logger emits, never widen it.
|
|
57
|
+
* Undefined means "every entry the module level lets through".
|
|
56
58
|
*/
|
|
57
59
|
readonly minLevel?: LogLevel;
|
|
58
60
|
/**
|
|
@@ -64,8 +66,13 @@ interface ILogDestination {
|
|
|
64
66
|
* - Buffer and flush periodically (e.g., HTTP batching)
|
|
65
67
|
* - Return a Promise for async I/O
|
|
66
68
|
*
|
|
67
|
-
* Errors thrown here
|
|
68
|
-
*
|
|
69
|
+
* Errors thrown here, and rejections of a returned promise, are caught by the
|
|
70
|
+
* library: the entry is dropped for this destination only, and one failure line
|
|
71
|
+
* is written to stderr per failed entry. There is no retry and no circuit
|
|
72
|
+
* breaker, so a destination that initialized and keeps failing keeps receiving
|
|
73
|
+
* entries and keeps being reported; one that needs to back off does so itself.
|
|
74
|
+
* (A destination whose `onInit()` failed is a different case: it receives no
|
|
75
|
+
* entries at all.)
|
|
69
76
|
*
|
|
70
77
|
* @param payload — Newline-terminated JSON entry, UTF-8 encoded.
|
|
71
78
|
*/
|
|
@@ -250,30 +257,35 @@ interface EmittedDeploymentResource {
|
|
|
250
257
|
* Every field is optional; defaults are applied by `applyDefaults`.
|
|
251
258
|
*/
|
|
252
259
|
interface HttpOptions {
|
|
253
|
-
/**
|
|
260
|
+
/**
|
|
261
|
+
* Mount the HTTP interceptor + filter. Default: false. The filter is registered by
|
|
262
|
+
* `forRoot` only: `forRootAsync` registers the interceptor and leaves the filter to
|
|
263
|
+
* the consumer (`{ provide: APP_FILTER, useClass: HttpExceptionFilter }`).
|
|
264
|
+
*/
|
|
254
265
|
isEnabled?: boolean;
|
|
255
|
-
/**
|
|
266
|
+
/**
|
|
267
|
+
* Capture unhandled HTTP exceptions and emit `HTTP_EXCEPTION_UNHANDLED`. Default: true.
|
|
268
|
+
* Read by `forRoot` only; it has no effect on `forRootAsync`, which never registers the
|
|
269
|
+
* filter.
|
|
270
|
+
*/
|
|
256
271
|
shouldCaptureExceptions?: boolean;
|
|
257
272
|
/** Generate `requestId` when the request header is absent. Default: true. */
|
|
258
273
|
shouldGenerateRequestId?: boolean;
|
|
259
274
|
/**
|
|
260
275
|
* Paths that bypass HTTP logging (health checks, metrics). Defaults:
|
|
261
|
-
* `/health`, `/metrics`.
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
* a prefix pattern would silently swallow a consumer's own `/health/*` route
|
|
275
|
-
* that they did want logged. Excluding a path is a decision about which requests
|
|
276
|
-
* disappear from the record, so it stays explicit.
|
|
276
|
+
* `/health`, `/health/live`, `/health/ready`, `/metrics`.
|
|
277
|
+
*
|
|
278
|
+
* `/health/live` and `/health/ready` are the liveness and readiness probes of
|
|
279
|
+
* `@bymax-one/nest-core`, which serves no bare `/health`; left logged, a probe hit
|
|
280
|
+
* every few seconds drowns the record (measured on one deployment: 8272 of 8274
|
|
281
|
+
* HTTP entries were the liveness probe). Each default is an exact, anchored path,
|
|
282
|
+
* not a prefix, so a consumer's own `/health/other` route is still logged.
|
|
283
|
+
*
|
|
284
|
+
* The prefixes are CONFIGURABLE in nest-core (`DEFAULT_HEALTH_PATH`,
|
|
285
|
+
* `DEFAULT_METRICS_PATH`): when they are changed, or when the probes should be
|
|
286
|
+
* logged, set `excludePaths` yourself. A value REPLACES the defaults rather than
|
|
287
|
+
* adding to them; `[/^\/health$/, /^\/metrics$/]` excludes only `/health` and
|
|
288
|
+
* `/metrics`.
|
|
277
289
|
*
|
|
278
290
|
* SECURITY: each pattern is `.test()`-ed against the (attacker-controllable)
|
|
279
291
|
* request path on every request. Supply only anchored, linear-time regexes
|
|
@@ -1171,6 +1183,8 @@ declare class PrettyDevDestination implements ILogDestination {
|
|
|
1171
1183
|
* Lazily resolve `pino-pretty` and build the colorized transform that renders
|
|
1172
1184
|
* each entry to `process.stdout`.
|
|
1173
1185
|
*
|
|
1186
|
+
* @throws Error When `NODE_ENV` is `production`: this sink is development
|
|
1187
|
+
* output, so it refuses rather than put ANSI colour into the log stream.
|
|
1174
1188
|
* @throws Error When `pino-pretty` is not installed — with an actionable
|
|
1175
1189
|
* message telling the consumer to install it or drop this destination.
|
|
1176
1190
|
*/
|
|
@@ -1773,6 +1787,11 @@ declare const LOG_CONTEXT_METADATA_KEY = "bymax_logger:log_context";
|
|
|
1773
1787
|
* Set the log-context label for a class. All loggers injected within the class
|
|
1774
1788
|
* are intended to adopt this label (auto-application requires a wired interceptor).
|
|
1775
1789
|
*
|
|
1790
|
+
* @deprecated Nothing in this package reads the metadata, so the label is never
|
|
1791
|
+
* applied: no interceptor is wired. Inject a logger bound to the label with
|
|
1792
|
+
* `@InjectLogger(PaymentsService.name)` instead. Do not call `setContext()` on
|
|
1793
|
+
* the shared logger — it is a singleton, so that overwrites the label for every
|
|
1794
|
+
* other holder.
|
|
1776
1795
|
* @param name - The context label (typically the class name).
|
|
1777
1796
|
* @returns A NestJS metadata decorator carrying the label.
|
|
1778
1797
|
* @example
|
|
@@ -1816,9 +1835,31 @@ declare const LOGGER_OPTIONS_TOKEN: unique symbol;
|
|
|
1816
1835
|
declare const LOGGER_PINO_INSTANCE_TOKEN: unique symbol;
|
|
1817
1836
|
/** Injection token for the resolved destinations array (read-only at runtime). */
|
|
1818
1837
|
declare const LOGGER_DESTINATIONS_TOKEN: unique symbol;
|
|
1838
|
+
/**
|
|
1839
|
+
* Injection token for the redaction function backing the DEFAULT PII coverage.
|
|
1840
|
+
*
|
|
1841
|
+
* Exposed as its own provider because `PinoLoggerService.child()` has to apply
|
|
1842
|
+
* it BEFORE handing bindings to Pino: child bindings are pre-serialized into the
|
|
1843
|
+
* instance's `chindings` fragment and never reach `formatters.log`, so the
|
|
1844
|
+
* factory's hook cannot see them.
|
|
1845
|
+
*/
|
|
1846
|
+
declare const LOGGER_REDACTOR_TOKEN: unique symbol;
|
|
1819
1847
|
/** Injection token for the per-request AsyncLocalStorage-backed log context. */
|
|
1820
1848
|
declare const LOG_CONTEXT_TOKEN: unique symbol;
|
|
1821
1849
|
|
|
1850
|
+
/**
|
|
1851
|
+
* The canonical sensitive-field-name set.
|
|
1852
|
+
*
|
|
1853
|
+
* Under the default `redactStrategy: 'names'` this list IS the redaction
|
|
1854
|
+
* contract: a value is censored when its key name appears here, at any depth.
|
|
1855
|
+
* Under the legacy `'paths'` strategy the same names are expanded to depths 1–4
|
|
1856
|
+
* by {@link depth} to build {@link DEFAULT_REDACT_PATHS}.
|
|
1857
|
+
*
|
|
1858
|
+
* Append-only: a name may be added in a minor release; removing one requires a
|
|
1859
|
+
* major, because a field that silently stopped being redacted is a leak no
|
|
1860
|
+
* consumer would notice.
|
|
1861
|
+
*/
|
|
1862
|
+
declare const REDACT_COMMON_FIELDS: readonly string[];
|
|
1822
1863
|
/**
|
|
1823
1864
|
* The legacy `fast-redact` path expansion of {@link REDACT_COMMON_FIELDS}:
|
|
1824
1865
|
* every name at the record root plus wildcard depths 1–4, followed by the
|
|
@@ -1837,6 +1878,17 @@ declare const LOG_CONTEXT_TOKEN: unique symbol;
|
|
|
1837
1878
|
*/
|
|
1838
1879
|
declare const DEFAULT_REDACT_PATHS: readonly string[];
|
|
1839
1880
|
|
|
1881
|
+
/**
|
|
1882
|
+
* Check whether a trace ID is a valid, non-zero W3C Trace Context value.
|
|
1883
|
+
*
|
|
1884
|
+
* Enforces 32 lowercase hex characters (rejecting non-hex input that could
|
|
1885
|
+
* carry log-injection payloads) and rejects the all-zeros "no trace" sentinel.
|
|
1886
|
+
*
|
|
1887
|
+
* @param traceId - The candidate trace ID.
|
|
1888
|
+
* @returns `true` when the ID is 32 lowercase hex chars and not all zeros.
|
|
1889
|
+
*/
|
|
1890
|
+
declare function isValidTraceId(traceId: string): boolean;
|
|
1891
|
+
|
|
1840
1892
|
/**
|
|
1841
1893
|
* Pino numeric level mapping. Frozen at runtime so destinations and the
|
|
1842
1894
|
* NestJS bridge can rely on a stable wire-format contract.
|
|
@@ -1944,4 +1996,5 @@ interface LogEntry {
|
|
|
1944
1996
|
*/
|
|
1945
1997
|
declare const LOG_KEYS_CONVENTION_REGEX: RegExp;
|
|
1946
1998
|
|
|
1947
|
-
export { BymaxLoggerModule,
|
|
1999
|
+
export { BymaxLoggerModule, DEFAULT_REDACT_PATHS, DefaultStdoutDestination, HttpAccessLogMiddleware, HttpExceptionFilter, HttpLoggingInterceptor, InjectLogger, LOGGER_DESTINATIONS_TOKEN, LOGGER_OPTIONS_TOKEN, LOGGER_PINO_INSTANCE_TOKEN, LOGGER_REDACTOR_TOKEN, LOG_CONTEXT_METADATA_KEY, LOG_CONTEXT_TOKEN, LOG_KEYS_CONVENTION_REGEX, LogContext, LogContextService, LogPerformance, PINO_LEVEL_NAMES, PINO_LEVEL_NUMBERS, PinoLoggerService, PinoUnexpectedErrorSink, PrettyDevDestination, REDACT_COMMON_FIELDS, RESERVED_LOG_KEYS, RESERVED_LOG_KEYS_NOT_EMITTED, RequestIdMiddleware, applyAccessLog, applyRequestIdMiddleware, isValidTraceId };
|
|
2000
|
+
export type { BymaxLoggerModuleAsyncOptions, BymaxLoggerModuleOptions, BymaxLoggerModuleOptionsFactory, EmittedDeploymentResource, EmittedServiceResource, HttpOptions, ILogDestination, IUnexpectedErrorSink, IncomingHeaders, LogContext$1 as LogContextBag, LogEntry, LogLevel, LoggableRequest, LoggableResponse, NextHandler, OtelOptions, PrettyViewOptions, ReservedLogKey, ResolvedBymaxLoggerModuleOptions, ResolvedServiceMetadata, ServiceMetadata, UnexpectedErrorReport };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -52,7 +52,9 @@ interface ILogDestination {
|
|
|
52
52
|
readonly name: string;
|
|
53
53
|
/**
|
|
54
54
|
* Minimum log level this destination accepts. Entries below this level
|
|
55
|
-
* are not written.
|
|
55
|
+
* are not written. The effective level is the STRICTER of this and the module
|
|
56
|
+
* `level`, so it can only narrow what the logger emits, never widen it.
|
|
57
|
+
* Undefined means "every entry the module level lets through".
|
|
56
58
|
*/
|
|
57
59
|
readonly minLevel?: LogLevel;
|
|
58
60
|
/**
|
|
@@ -64,8 +66,13 @@ interface ILogDestination {
|
|
|
64
66
|
* - Buffer and flush periodically (e.g., HTTP batching)
|
|
65
67
|
* - Return a Promise for async I/O
|
|
66
68
|
*
|
|
67
|
-
* Errors thrown here
|
|
68
|
-
*
|
|
69
|
+
* Errors thrown here, and rejections of a returned promise, are caught by the
|
|
70
|
+
* library: the entry is dropped for this destination only, and one failure line
|
|
71
|
+
* is written to stderr per failed entry. There is no retry and no circuit
|
|
72
|
+
* breaker, so a destination that initialized and keeps failing keeps receiving
|
|
73
|
+
* entries and keeps being reported; one that needs to back off does so itself.
|
|
74
|
+
* (A destination whose `onInit()` failed is a different case: it receives no
|
|
75
|
+
* entries at all.)
|
|
69
76
|
*
|
|
70
77
|
* @param payload — Newline-terminated JSON entry, UTF-8 encoded.
|
|
71
78
|
*/
|
|
@@ -250,30 +257,35 @@ interface EmittedDeploymentResource {
|
|
|
250
257
|
* Every field is optional; defaults are applied by `applyDefaults`.
|
|
251
258
|
*/
|
|
252
259
|
interface HttpOptions {
|
|
253
|
-
/**
|
|
260
|
+
/**
|
|
261
|
+
* Mount the HTTP interceptor + filter. Default: false. The filter is registered by
|
|
262
|
+
* `forRoot` only: `forRootAsync` registers the interceptor and leaves the filter to
|
|
263
|
+
* the consumer (`{ provide: APP_FILTER, useClass: HttpExceptionFilter }`).
|
|
264
|
+
*/
|
|
254
265
|
isEnabled?: boolean;
|
|
255
|
-
/**
|
|
266
|
+
/**
|
|
267
|
+
* Capture unhandled HTTP exceptions and emit `HTTP_EXCEPTION_UNHANDLED`. Default: true.
|
|
268
|
+
* Read by `forRoot` only; it has no effect on `forRootAsync`, which never registers the
|
|
269
|
+
* filter.
|
|
270
|
+
*/
|
|
256
271
|
shouldCaptureExceptions?: boolean;
|
|
257
272
|
/** Generate `requestId` when the request header is absent. Default: true. */
|
|
258
273
|
shouldGenerateRequestId?: boolean;
|
|
259
274
|
/**
|
|
260
275
|
* Paths that bypass HTTP logging (health checks, metrics). Defaults:
|
|
261
|
-
* `/health`, `/metrics`.
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
* a prefix pattern would silently swallow a consumer's own `/health/*` route
|
|
275
|
-
* that they did want logged. Excluding a path is a decision about which requests
|
|
276
|
-
* disappear from the record, so it stays explicit.
|
|
276
|
+
* `/health`, `/health/live`, `/health/ready`, `/metrics`.
|
|
277
|
+
*
|
|
278
|
+
* `/health/live` and `/health/ready` are the liveness and readiness probes of
|
|
279
|
+
* `@bymax-one/nest-core`, which serves no bare `/health`; left logged, a probe hit
|
|
280
|
+
* every few seconds drowns the record (measured on one deployment: 8272 of 8274
|
|
281
|
+
* HTTP entries were the liveness probe). Each default is an exact, anchored path,
|
|
282
|
+
* not a prefix, so a consumer's own `/health/other` route is still logged.
|
|
283
|
+
*
|
|
284
|
+
* The prefixes are CONFIGURABLE in nest-core (`DEFAULT_HEALTH_PATH`,
|
|
285
|
+
* `DEFAULT_METRICS_PATH`): when they are changed, or when the probes should be
|
|
286
|
+
* logged, set `excludePaths` yourself. A value REPLACES the defaults rather than
|
|
287
|
+
* adding to them; `[/^\/health$/, /^\/metrics$/]` excludes only `/health` and
|
|
288
|
+
* `/metrics`.
|
|
277
289
|
*
|
|
278
290
|
* SECURITY: each pattern is `.test()`-ed against the (attacker-controllable)
|
|
279
291
|
* request path on every request. Supply only anchored, linear-time regexes
|
|
@@ -1171,6 +1183,8 @@ declare class PrettyDevDestination implements ILogDestination {
|
|
|
1171
1183
|
* Lazily resolve `pino-pretty` and build the colorized transform that renders
|
|
1172
1184
|
* each entry to `process.stdout`.
|
|
1173
1185
|
*
|
|
1186
|
+
* @throws Error When `NODE_ENV` is `production`: this sink is development
|
|
1187
|
+
* output, so it refuses rather than put ANSI colour into the log stream.
|
|
1174
1188
|
* @throws Error When `pino-pretty` is not installed — with an actionable
|
|
1175
1189
|
* message telling the consumer to install it or drop this destination.
|
|
1176
1190
|
*/
|
|
@@ -1773,6 +1787,11 @@ declare const LOG_CONTEXT_METADATA_KEY = "bymax_logger:log_context";
|
|
|
1773
1787
|
* Set the log-context label for a class. All loggers injected within the class
|
|
1774
1788
|
* are intended to adopt this label (auto-application requires a wired interceptor).
|
|
1775
1789
|
*
|
|
1790
|
+
* @deprecated Nothing in this package reads the metadata, so the label is never
|
|
1791
|
+
* applied: no interceptor is wired. Inject a logger bound to the label with
|
|
1792
|
+
* `@InjectLogger(PaymentsService.name)` instead. Do not call `setContext()` on
|
|
1793
|
+
* the shared logger — it is a singleton, so that overwrites the label for every
|
|
1794
|
+
* other holder.
|
|
1776
1795
|
* @param name - The context label (typically the class name).
|
|
1777
1796
|
* @returns A NestJS metadata decorator carrying the label.
|
|
1778
1797
|
* @example
|
|
@@ -1816,9 +1835,31 @@ declare const LOGGER_OPTIONS_TOKEN: unique symbol;
|
|
|
1816
1835
|
declare const LOGGER_PINO_INSTANCE_TOKEN: unique symbol;
|
|
1817
1836
|
/** Injection token for the resolved destinations array (read-only at runtime). */
|
|
1818
1837
|
declare const LOGGER_DESTINATIONS_TOKEN: unique symbol;
|
|
1838
|
+
/**
|
|
1839
|
+
* Injection token for the redaction function backing the DEFAULT PII coverage.
|
|
1840
|
+
*
|
|
1841
|
+
* Exposed as its own provider because `PinoLoggerService.child()` has to apply
|
|
1842
|
+
* it BEFORE handing bindings to Pino: child bindings are pre-serialized into the
|
|
1843
|
+
* instance's `chindings` fragment and never reach `formatters.log`, so the
|
|
1844
|
+
* factory's hook cannot see them.
|
|
1845
|
+
*/
|
|
1846
|
+
declare const LOGGER_REDACTOR_TOKEN: unique symbol;
|
|
1819
1847
|
/** Injection token for the per-request AsyncLocalStorage-backed log context. */
|
|
1820
1848
|
declare const LOG_CONTEXT_TOKEN: unique symbol;
|
|
1821
1849
|
|
|
1850
|
+
/**
|
|
1851
|
+
* The canonical sensitive-field-name set.
|
|
1852
|
+
*
|
|
1853
|
+
* Under the default `redactStrategy: 'names'` this list IS the redaction
|
|
1854
|
+
* contract: a value is censored when its key name appears here, at any depth.
|
|
1855
|
+
* Under the legacy `'paths'` strategy the same names are expanded to depths 1–4
|
|
1856
|
+
* by {@link depth} to build {@link DEFAULT_REDACT_PATHS}.
|
|
1857
|
+
*
|
|
1858
|
+
* Append-only: a name may be added in a minor release; removing one requires a
|
|
1859
|
+
* major, because a field that silently stopped being redacted is a leak no
|
|
1860
|
+
* consumer would notice.
|
|
1861
|
+
*/
|
|
1862
|
+
declare const REDACT_COMMON_FIELDS: readonly string[];
|
|
1822
1863
|
/**
|
|
1823
1864
|
* The legacy `fast-redact` path expansion of {@link REDACT_COMMON_FIELDS}:
|
|
1824
1865
|
* every name at the record root plus wildcard depths 1–4, followed by the
|
|
@@ -1837,6 +1878,17 @@ declare const LOG_CONTEXT_TOKEN: unique symbol;
|
|
|
1837
1878
|
*/
|
|
1838
1879
|
declare const DEFAULT_REDACT_PATHS: readonly string[];
|
|
1839
1880
|
|
|
1881
|
+
/**
|
|
1882
|
+
* Check whether a trace ID is a valid, non-zero W3C Trace Context value.
|
|
1883
|
+
*
|
|
1884
|
+
* Enforces 32 lowercase hex characters (rejecting non-hex input that could
|
|
1885
|
+
* carry log-injection payloads) and rejects the all-zeros "no trace" sentinel.
|
|
1886
|
+
*
|
|
1887
|
+
* @param traceId - The candidate trace ID.
|
|
1888
|
+
* @returns `true` when the ID is 32 lowercase hex chars and not all zeros.
|
|
1889
|
+
*/
|
|
1890
|
+
declare function isValidTraceId(traceId: string): boolean;
|
|
1891
|
+
|
|
1840
1892
|
/**
|
|
1841
1893
|
* Pino numeric level mapping. Frozen at runtime so destinations and the
|
|
1842
1894
|
* NestJS bridge can rely on a stable wire-format contract.
|
|
@@ -1944,4 +1996,5 @@ interface LogEntry {
|
|
|
1944
1996
|
*/
|
|
1945
1997
|
declare const LOG_KEYS_CONVENTION_REGEX: RegExp;
|
|
1946
1998
|
|
|
1947
|
-
export { BymaxLoggerModule,
|
|
1999
|
+
export { BymaxLoggerModule, DEFAULT_REDACT_PATHS, DefaultStdoutDestination, HttpAccessLogMiddleware, HttpExceptionFilter, HttpLoggingInterceptor, InjectLogger, LOGGER_DESTINATIONS_TOKEN, LOGGER_OPTIONS_TOKEN, LOGGER_PINO_INSTANCE_TOKEN, LOGGER_REDACTOR_TOKEN, LOG_CONTEXT_METADATA_KEY, LOG_CONTEXT_TOKEN, LOG_KEYS_CONVENTION_REGEX, LogContext, LogContextService, LogPerformance, PINO_LEVEL_NAMES, PINO_LEVEL_NUMBERS, PinoLoggerService, PinoUnexpectedErrorSink, PrettyDevDestination, REDACT_COMMON_FIELDS, RESERVED_LOG_KEYS, RESERVED_LOG_KEYS_NOT_EMITTED, RequestIdMiddleware, applyAccessLog, applyRequestIdMiddleware, isValidTraceId };
|
|
2000
|
+
export type { BymaxLoggerModuleAsyncOptions, BymaxLoggerModuleOptions, BymaxLoggerModuleOptionsFactory, EmittedDeploymentResource, EmittedServiceResource, HttpOptions, ILogDestination, IUnexpectedErrorSink, IncomingHeaders, LogContext$1 as LogContextBag, LogEntry, LogLevel, LoggableRequest, LoggableResponse, NextHandler, OtelOptions, PrettyViewOptions, ReservedLogKey, ResolvedBymaxLoggerModuleOptions, ResolvedServiceMetadata, ServiceMetadata, UnexpectedErrorReport };
|