ntlogger 2.9.1 → 3.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/README.md +237 -2
- package/index.d.ts +192 -183
- package/lib/lifecycle.js +53 -0
- package/lib/logger.js +100 -57
- package/lib/pinoHooks.js +446 -0
- package/lib/processHandlers.js +305 -0
- package/lib/secretRedaction.js +675 -0
- package/lib/signalHandler.js +31 -48
- package/package.json +69 -13
- package/pino.d.ts +287 -0
- package/pino.js +434 -13
- package/plugins/README.md +129 -1
- package/plugins/discord.js +30 -48
- package/plugins/index.js +27 -11
- package/plugins/lib/httpDelivery.js +98 -0
- package/plugins/lib/syslogClient.js +18 -8
- package/plugins/mysql.js +73 -83
- package/plugins/openobserve.js +33 -207
- package/plugins/otel.js +263 -0
- package/plugins/postgres.js +69 -67
- package/plugins/sentry.js +36 -3
- package/plugins/syslog.js +6 -9
- package/plugins/teams.js +16 -67
- package/transports/pino.js +188 -12
package/pino.js
CHANGED
|
@@ -1,28 +1,433 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @file pino.js
|
|
5
|
+
* @description Entry point for `require('ntlogger/pino')`.
|
|
6
|
+
*
|
|
7
|
+
* Exports (in addition to everything `transports/pino.js` exports):
|
|
8
|
+
* - createLogger(opts) pre-configured Pino logger with NTL conventions
|
|
9
|
+
* - isSilent(opts) silent-mode resolution, exported for testability
|
|
10
|
+
* - installProcessHandlers() re-export of lib/processHandlers.js
|
|
11
|
+
* - drainLogger() re-export of lib/processHandlers.js
|
|
12
|
+
*
|
|
13
|
+
* The default export is the transport factory itself, so
|
|
14
|
+
* `transport: { target: 'ntlogger/pino' }` keeps working.
|
|
15
|
+
*
|
|
16
|
+
* NOTE: lib/processHandlers.js is required here and *never* from
|
|
17
|
+
* transports/pino.js — the transport runs inside Pino's worker thread, where
|
|
18
|
+
* installing process handlers would be meaningless and harmful.
|
|
19
|
+
*/
|
|
20
|
+
|
|
3
21
|
// Re-export the transport for use with Pino's transport option
|
|
4
22
|
// Usage: transport: { target: 'ntlogger/pino' }
|
|
5
23
|
module.exports = require('./transports/pino');
|
|
6
24
|
|
|
7
|
-
|
|
8
|
-
|
|
25
|
+
const { installProcessHandlers, drainLogger } = require('./lib/processHandlers');
|
|
26
|
+
|
|
27
|
+
module.exports.installProcessHandlers = installProcessHandlers;
|
|
28
|
+
module.exports.drainLogger = drainLogger;
|
|
29
|
+
|
|
30
|
+
/** Values of NTLOGGER_SILENT that force silent mode on/off. */
|
|
31
|
+
const SILENT_TRUE = new Set(['1', 'true', 'yes']);
|
|
32
|
+
const SILENT_FALSE = new Set(['0', 'false', 'no']);
|
|
33
|
+
|
|
34
|
+
/** Pino's default error key. A merge object's `err` supplies `msg` when none is given. */
|
|
35
|
+
const ERROR_KEY = 'err';
|
|
36
|
+
|
|
37
|
+
/** Default budget for `logger.close()`. */
|
|
38
|
+
const DEFAULT_CLOSE_TIMEOUT = 5000;
|
|
39
|
+
|
|
40
|
+
/** A string is treated as a Pino (fast-redact) path when it contains any of these. */
|
|
41
|
+
const PINO_PATH_CHARS = /[.[\]*]/;
|
|
42
|
+
|
|
43
|
+
let contextWarned = false;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Emit at most one process warning when a user-supplied context provider throws.
|
|
47
|
+
* @param {Error} err - The swallowed error.
|
|
48
|
+
* @returns {void}
|
|
49
|
+
*/
|
|
50
|
+
function warnContextOnce(err) {
|
|
51
|
+
if (contextWarned) return;
|
|
52
|
+
contextWarned = true;
|
|
53
|
+
try {
|
|
54
|
+
process.emitWarning(
|
|
55
|
+
'contextProvider threw, context dropped for this and future failures: ' + ((err && err.message) || err),
|
|
56
|
+
'NTLoggerContextWarning'
|
|
57
|
+
);
|
|
58
|
+
} catch (_ignored) {
|
|
59
|
+
// Warning is best-effort.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Plain object / array test. Used to decide whether an interpolation argument
|
|
65
|
+
* is worth deep-redacting (Errors and class instances are left alone so their
|
|
66
|
+
* `util.format` rendering is unchanged).
|
|
67
|
+
* @param {*} value - Candidate value.
|
|
68
|
+
* @returns {boolean} - True for array literals and plain objects.
|
|
69
|
+
*/
|
|
70
|
+
function isPlainContainer(value) {
|
|
71
|
+
if (value === null || typeof value !== 'object') return false;
|
|
72
|
+
if (Array.isArray(value)) return true;
|
|
73
|
+
const proto = Object.getPrototypeOf(value);
|
|
74
|
+
return proto === Object.prototype || proto === null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Resolve silent mode.
|
|
79
|
+
*
|
|
80
|
+
* Precedence:
|
|
81
|
+
* 1. `opts.silent` when it is a boolean
|
|
82
|
+
* 2. `NTLOGGER_SILENT` env var (`1|true|yes` / `0|false|no`, case-insensitive)
|
|
83
|
+
* 3. `NODE_ENV === 'test'`
|
|
84
|
+
*
|
|
85
|
+
* Anything else (unset, or an unrecognised env value) falls through to the next step.
|
|
86
|
+
*
|
|
87
|
+
* @param {object} [opts] - createLogger options.
|
|
88
|
+
* @returns {boolean} - True when the logger must emit nothing.
|
|
89
|
+
*/
|
|
90
|
+
function isSilent(opts = {}) {
|
|
91
|
+
if (typeof opts.silent === 'boolean') return opts.silent;
|
|
92
|
+
|
|
93
|
+
const raw = process.env.NTLOGGER_SILENT;
|
|
94
|
+
if (typeof raw === 'string' && raw.length > 0) {
|
|
95
|
+
const value = raw.trim().toLowerCase();
|
|
96
|
+
if (SILENT_TRUE.has(value)) return true;
|
|
97
|
+
if (SILENT_FALSE.has(value)) return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return process.env.NODE_ENV === 'test';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Normalize `opts.redact` into a local redactor plus (optionally) paths that are
|
|
105
|
+
* forwarded to Pino's own `redact` option.
|
|
106
|
+
*
|
|
107
|
+
* Accepted shapes:
|
|
108
|
+
* false -> redaction disabled entirely
|
|
109
|
+
* undefined -> defaults from lib/secretRedaction.js
|
|
110
|
+
* string[] -> entries containing `.`/`[`/`]`/`*` become Pino redact paths,
|
|
111
|
+
* the rest become `extraKeys` for the local redactor
|
|
112
|
+
* object -> passed straight to createRedactor()
|
|
113
|
+
*
|
|
114
|
+
* @param {*} redact - Raw option value.
|
|
115
|
+
* @returns {{redactor: (object|null), paths: (string[]|null)}} - Resolved redaction.
|
|
116
|
+
*/
|
|
117
|
+
function resolveRedaction(redact) {
|
|
118
|
+
if (redact === false) return { redactor: null, paths: null };
|
|
119
|
+
|
|
120
|
+
const { createRedactor } = require('./lib/secretRedaction');
|
|
121
|
+
|
|
122
|
+
if (Array.isArray(redact)) {
|
|
123
|
+
const paths = [];
|
|
124
|
+
const extraKeys = [];
|
|
125
|
+
for (const entry of redact) {
|
|
126
|
+
if (typeof entry !== 'string' || entry.length === 0) continue;
|
|
127
|
+
if (PINO_PATH_CHARS.test(entry)) paths.push(entry);
|
|
128
|
+
else extraKeys.push(entry);
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
redactor: createRedactor(extraKeys.length > 0 ? { extraKeys } : undefined),
|
|
132
|
+
paths: paths.length > 0 ? paths : null,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (redact !== null && typeof redact === 'object') {
|
|
137
|
+
return { redactor: createRedactor(redact), paths: null };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return { redactor: createRedactor(), paths: null };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Redact the free-form string parts of a log call, in place.
|
|
145
|
+
*
|
|
146
|
+
* `formatters.log` only sees the merged object, never the message string or its
|
|
147
|
+
* interpolation arguments, so those have to be handled in the `logMethod` hook.
|
|
148
|
+
* Pino builds a fresh array for every call, so mutating `args` is safe.
|
|
149
|
+
*
|
|
150
|
+
* Two cases need an explicit message to be *inserted*: `log.error(err)` and
|
|
151
|
+
* `log.error({ err })` both take `msg` from `err.message`, which bypasses
|
|
152
|
+
* `formatters.log`. Inserting is skipped when redaction is a no-op so ordinary
|
|
153
|
+
* calls keep their exact original shape.
|
|
154
|
+
*
|
|
155
|
+
* @param {Array} args - Arguments Pino passed to `hooks.logMethod`.
|
|
156
|
+
* @param {object} redactor - Redactor from lib/secretRedaction.js.
|
|
157
|
+
* @returns {Array} - The same array, redacted.
|
|
158
|
+
*/
|
|
159
|
+
function redactLogArgs(args, redactor) {
|
|
160
|
+
if (!args || args.length === 0) return args;
|
|
161
|
+
|
|
162
|
+
const first = args[0];
|
|
163
|
+
let start = 0;
|
|
164
|
+
|
|
165
|
+
if (first instanceof Error) {
|
|
166
|
+
start = 1;
|
|
167
|
+
if (typeof args[1] !== 'string' && typeof first.message === 'string') {
|
|
168
|
+
const redacted = redactor.redactString(first.message);
|
|
169
|
+
if (redacted !== first.message) args.splice(1, 0, redacted);
|
|
170
|
+
}
|
|
171
|
+
} else if (first !== null && typeof first === 'object' && !Array.isArray(first)) {
|
|
172
|
+
// Merge object: its own keys are handled by formatters.log.
|
|
173
|
+
start = 1;
|
|
174
|
+
if (typeof args[1] !== 'string'
|
|
175
|
+
&& first.msg === undefined
|
|
176
|
+
&& first[ERROR_KEY] instanceof Error
|
|
177
|
+
&& typeof first[ERROR_KEY].message === 'string') {
|
|
178
|
+
const redacted = redactor.redactString(first[ERROR_KEY].message);
|
|
179
|
+
if (redacted !== first[ERROR_KEY].message) args.splice(1, 0, redacted);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
for (let i = start; i < args.length; i++) {
|
|
184
|
+
const arg = args[i];
|
|
185
|
+
if (typeof arg === 'string') args[i] = redactor.redactString(arg);
|
|
186
|
+
else if (isPlainContainer(arg)) args[i] = redactor.redactObject(arg);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return args;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Redact a merge object for `formatters.log`.
|
|
194
|
+
*
|
|
195
|
+
* `formatters.log` runs *before* Pino's serializers (see `_asJson` in
|
|
196
|
+
* pino/lib/tools.js), so a real `Error` under the error key must survive this
|
|
197
|
+
* pass untouched — otherwise Pino's `err` serializer receives a plain object
|
|
198
|
+
* and reports `"type":"Object"`. The error itself is redacted by the `err`
|
|
199
|
+
* serializer installed alongside this formatter.
|
|
200
|
+
*
|
|
201
|
+
* Errors under any *other* key are still deep-redacted here, which is strictly
|
|
202
|
+
* better than Pino's default (`JSON.stringify(new Error())` is `{}`).
|
|
203
|
+
*
|
|
204
|
+
* @param {object} obj - Merged log object.
|
|
205
|
+
* @param {object} redactor - Redactor from lib/secretRedaction.js.
|
|
206
|
+
* @returns {object} - Redacted copy.
|
|
207
|
+
*/
|
|
208
|
+
function redactMergeObject(obj, redactor) {
|
|
209
|
+
if (obj === null || typeof obj !== 'object' || !(obj[ERROR_KEY] instanceof Error)) {
|
|
210
|
+
return redactor.redactObject(obj);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const keys = Object.keys(obj);
|
|
214
|
+
const stripped = {};
|
|
215
|
+
for (const key of keys) {
|
|
216
|
+
if (key !== ERROR_KEY) stripped[key] = obj[key];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const redacted = redactor.redactObject(stripped);
|
|
220
|
+
const out = {};
|
|
221
|
+
for (const key of keys) {
|
|
222
|
+
out[key] = key === ERROR_KEY ? obj[key] : redacted[key];
|
|
223
|
+
}
|
|
224
|
+
return out;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Build the single `hooks.logMethod` Pino allows, composing string redaction
|
|
229
|
+
* with the sampling/rate-limit/dedup pipeline from lib/pinoHooks.js.
|
|
230
|
+
*
|
|
231
|
+
* @param {object|null} redactor - Redactor, or null when redaction is off.
|
|
232
|
+
* @param {object|null} features - Feature adapter, or null when nothing is configured.
|
|
233
|
+
* @returns {function|null} - Hook function, or null when neither is active.
|
|
234
|
+
*/
|
|
235
|
+
function composeLogMethod(redactor, features) {
|
|
236
|
+
const featureLog = features && features.enabled ? features.logMethod : null;
|
|
237
|
+
if (!redactor && !featureLog) return null;
|
|
238
|
+
|
|
239
|
+
return function logMethod(args, method, level) {
|
|
240
|
+
if (redactor) redactLogArgs(args, redactor);
|
|
241
|
+
if (featureLog) return featureLog.call(this, args, method, level);
|
|
242
|
+
return method.apply(this, args);
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Attach ntlogger's additive API to a Pino logger.
|
|
248
|
+
*
|
|
249
|
+
* Everything is defined as a *non-enumerable own property* of the returned
|
|
250
|
+
* instance. Pino's `child()` does `Object.create(parent)`, so children inherit
|
|
251
|
+
* these through the prototype chain without anything being written to Pino's
|
|
252
|
+
* shared prototype (which would leak across unrelated Pino instances).
|
|
253
|
+
*
|
|
254
|
+
* `child()` itself is overridden so child bindings are redacted: Pino resets
|
|
255
|
+
* `formatters.bindings` to the identity function inside `child()`, so that
|
|
256
|
+
* formatter only ever sees `base`.
|
|
257
|
+
*
|
|
258
|
+
* @param {object} logger - Pino logger instance.
|
|
259
|
+
* @param {object|null} features - Feature adapter from lib/pinoHooks.js.
|
|
260
|
+
* @param {object|null} redactor - Redactor from lib/secretRedaction.js.
|
|
261
|
+
* @returns {object} - The same logger, decorated.
|
|
262
|
+
*/
|
|
263
|
+
function decorateLogger(logger, features, redactor) {
|
|
264
|
+
const define = (name, value) => {
|
|
265
|
+
Object.defineProperty(logger, name, {
|
|
266
|
+
value,
|
|
267
|
+
writable: true,
|
|
268
|
+
configurable: true,
|
|
269
|
+
enumerable: false,
|
|
270
|
+
});
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
define('getStats', function getStats() {
|
|
274
|
+
return features ? features.getStats() : { sampling: null, deduplication: null };
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
define('resetStats', function resetStats() {
|
|
278
|
+
if (features) features.resetStats();
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// Releases the sampler/deduplicator timers, then flushes whatever the
|
|
282
|
+
// destination still holds. Calling it on a child tears down the state shared
|
|
283
|
+
// with its parent — close the root logger.
|
|
284
|
+
define('close', function close(timeout) {
|
|
285
|
+
if (features) features.destroy();
|
|
286
|
+
return drainLogger(this, typeof timeout === 'number' ? timeout : DEFAULT_CLOSE_TIMEOUT);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
if (redactor) {
|
|
290
|
+
const baseChild = logger.child;
|
|
291
|
+
define('child', function child(bindings, options) {
|
|
292
|
+
const safe = bindings !== null && typeof bindings === 'object'
|
|
293
|
+
? redactor.redactObject(bindings)
|
|
294
|
+
: bindings;
|
|
295
|
+
return baseChild.call(this, safe, options);
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return logger;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Create a pre-configured Pino logger that follows NightTimeLogger conventions.
|
|
304
|
+
*
|
|
305
|
+
* Returns a *real* Pino logger, so the call signature is object-first:
|
|
306
|
+
* `log.info({ tenantId }, 'message')`.
|
|
307
|
+
*
|
|
308
|
+
* @param {object} [opts] - Options.
|
|
309
|
+
* @param {string} [opts.level] - Pino level. Defaults to `LOG_LEVEL`, else `debug` in dev / `info` in production.
|
|
310
|
+
* @param {string} [opts.module] - Module label added to every line and used as the transport's default label.
|
|
311
|
+
* @param {string} [opts.defaultModule] - Transport label fallback when `module` is not set.
|
|
312
|
+
* @param {boolean} [opts.colorize] - Forwarded to the NTL transport.
|
|
313
|
+
* @param {string} [opts.service] - Constant top-level `service` field (log-contract core field).
|
|
314
|
+
* @param {object} [opts.context] - Static top-level fields merged into every line.
|
|
315
|
+
* @param {function} [opts.contextProvider] - Called per log call; its object result is merged at top level.
|
|
316
|
+
* @param {string[]} [opts.contextKeys] - When set, only these keys are taken from the provider result.
|
|
317
|
+
* @param {false|string[]|object} [opts.redact] - Secret redaction. Defaults to on.
|
|
318
|
+
* @param {object} [opts.sampling] - Per-level sampling rates.
|
|
319
|
+
* @param {object} [opts.rateLimit] - Per-level rate limits.
|
|
320
|
+
* @param {object|boolean} [opts.deduplication] - Duplicate-message collapsing.
|
|
321
|
+
* @param {boolean} [opts.silent] - Force silent mode on/off (see isSilent()).
|
|
322
|
+
* @param {boolean|object} [opts.processHandlers] - Opt-in crash/signal handlers.
|
|
323
|
+
* @param {*} [opts.destination] - For tests/advanced use: a Pino destination stream. Replaces the NTL transport.
|
|
324
|
+
* @returns {object} - Pino logger with `getStats()`, `resetStats()`, `close()` and (optionally) `uninstallProcessHandlers`.
|
|
325
|
+
*/
|
|
326
|
+
function createLogger(opts = {}) {
|
|
9
327
|
const pino = require('pino');
|
|
10
328
|
const path = require('path');
|
|
329
|
+
const os = require('os');
|
|
11
330
|
|
|
331
|
+
const silent = isSilent(opts);
|
|
12
332
|
const isDev = process.env.NODE_ENV !== 'production';
|
|
13
|
-
const level =
|
|
333
|
+
const level = silent
|
|
334
|
+
? 'silent'
|
|
335
|
+
: (opts.level || process.env.LOG_LEVEL || (isDev ? 'debug' : 'info'));
|
|
14
336
|
|
|
15
|
-
const loggerOpts = {
|
|
16
|
-
|
|
17
|
-
|
|
337
|
+
const loggerOpts = { level };
|
|
338
|
+
|
|
339
|
+
// --- constant fields ---------------------------------------------------
|
|
340
|
+
if (typeof opts.service === 'string' && opts.service.length > 0) {
|
|
341
|
+
// `base` replaces Pino's default, so pid/hostname have to be restored.
|
|
342
|
+
loggerOpts.base = { pid: process.pid, hostname: os.hostname(), service: opts.service };
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// --- redaction / features (skipped entirely in silent mode) ------------
|
|
346
|
+
let redactor = null;
|
|
347
|
+
let features = null;
|
|
348
|
+
|
|
349
|
+
if (!silent) {
|
|
350
|
+
const redaction = resolveRedaction(opts.redact);
|
|
351
|
+
redactor = redaction.redactor;
|
|
352
|
+
if (redaction.paths) loggerOpts.redact = redaction.paths;
|
|
353
|
+
|
|
354
|
+
if (redactor) {
|
|
355
|
+
loggerOpts.formatters = {
|
|
356
|
+
log: obj => redactMergeObject(obj, redactor),
|
|
357
|
+
// Only ever sees `base`: Pino resets this formatter to the identity
|
|
358
|
+
// function inside child(). Child bindings are handled by the child()
|
|
359
|
+
// override in decorateLogger().
|
|
360
|
+
bindings: bindings => redactor.redactObject(bindings),
|
|
361
|
+
};
|
|
362
|
+
// Runs after formatters.log, on the untouched Error, so `type`/`stack`
|
|
363
|
+
// keep Pino's exact shape while their strings get scrubbed.
|
|
364
|
+
loggerOpts.serializers = {
|
|
365
|
+
err: value => (value instanceof Error
|
|
366
|
+
? redactor.redactObject(pino.stdSerializers.err(value))
|
|
367
|
+
: value),
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const { createPinoFeatures } = require('./lib/pinoHooks');
|
|
372
|
+
features = createPinoFeatures({
|
|
373
|
+
sampling: opts.sampling,
|
|
374
|
+
rateLimit: opts.rateLimit,
|
|
375
|
+
deduplication: opts.deduplication,
|
|
376
|
+
defaultLocation: opts.module || opts.defaultModule,
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
const logMethod = composeLogMethod(redactor, features);
|
|
380
|
+
if (logMethod) loggerOpts.hooks = { logMethod };
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// --- context mixin ------------------------------------------------------
|
|
384
|
+
const staticContext = opts.context !== null && typeof opts.context === 'object' && !Array.isArray(opts.context)
|
|
385
|
+
? Object.assign({}, opts.context)
|
|
386
|
+
: null;
|
|
387
|
+
const provider = typeof opts.contextProvider === 'function' ? opts.contextProvider : null;
|
|
388
|
+
const contextKeys = Array.isArray(opts.contextKeys) && opts.contextKeys.length > 0
|
|
389
|
+
? opts.contextKeys.slice()
|
|
390
|
+
: null;
|
|
18
391
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
392
|
+
if (!silent && (opts.module || staticContext || provider)) {
|
|
393
|
+
// Precedence, lowest to highest: base/service < module < context < contextProvider
|
|
394
|
+
// < fields passed on the log call itself (Pino's default mixin merge strategy
|
|
395
|
+
// assigns the merge object over the mixin output).
|
|
396
|
+
loggerOpts.mixin = () => {
|
|
397
|
+
const out = {};
|
|
398
|
+
if (opts.module) out.module = opts.module;
|
|
399
|
+
if (staticContext) Object.assign(out, staticContext);
|
|
400
|
+
|
|
401
|
+
if (provider) {
|
|
402
|
+
let dynamic;
|
|
403
|
+
try {
|
|
404
|
+
dynamic = provider();
|
|
405
|
+
} catch (err) {
|
|
406
|
+
warnContextOnce(err);
|
|
407
|
+
dynamic = null;
|
|
408
|
+
}
|
|
409
|
+
if (dynamic !== null && typeof dynamic === 'object' && !Array.isArray(dynamic)) {
|
|
410
|
+
if (contextKeys) {
|
|
411
|
+
for (const key of contextKeys) {
|
|
412
|
+
const value = dynamic[key];
|
|
413
|
+
if (value !== undefined) out[key] = value;
|
|
414
|
+
}
|
|
415
|
+
} else {
|
|
416
|
+
Object.assign(out, dynamic);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return out;
|
|
422
|
+
};
|
|
22
423
|
}
|
|
23
424
|
|
|
24
|
-
//
|
|
25
|
-
|
|
425
|
+
// --- destination / transport -------------------------------------------
|
|
426
|
+
// `opts.destination` and `transport` are mutually exclusive in Pino, so an
|
|
427
|
+
// explicit destination always wins and the NTL formatter is skipped.
|
|
428
|
+
const destination = opts.destination !== undefined && opts.destination !== null ? opts.destination : null;
|
|
429
|
+
|
|
430
|
+
if (!silent && !destination && isDev) {
|
|
26
431
|
loggerOpts.transport = {
|
|
27
432
|
target: path.join(__dirname, 'transports', 'pino.js'),
|
|
28
433
|
options: {
|
|
@@ -32,5 +437,21 @@ module.exports.createLogger = function (opts = {}) {
|
|
|
32
437
|
};
|
|
33
438
|
}
|
|
34
439
|
|
|
35
|
-
|
|
36
|
-
|
|
440
|
+
const logger = destination ? pino(loggerOpts, destination) : pino(loggerOpts);
|
|
441
|
+
decorateLogger(logger, features, redactor);
|
|
442
|
+
|
|
443
|
+
if (opts.processHandlers) {
|
|
444
|
+
const handlerOpts = typeof opts.processHandlers === 'object' ? opts.processHandlers : {};
|
|
445
|
+
Object.defineProperty(logger, 'uninstallProcessHandlers', {
|
|
446
|
+
value: installProcessHandlers(logger, handlerOpts),
|
|
447
|
+
writable: true,
|
|
448
|
+
configurable: true,
|
|
449
|
+
enumerable: false,
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return logger;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
module.exports.createLogger = createLogger;
|
|
457
|
+
module.exports.isSilent = isSilent;
|