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/lib/pinoHooks.js
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file /lib/pinoHooks.js
|
|
5
|
+
* @description Pino adapter that exposes NightTimeLogger's sampling, rate limiting and
|
|
6
|
+
* deduplication features (lib/logSampler.js, lib/logDeduplicator.js) through a
|
|
7
|
+
* Pino `hooks.logMethod` implementation.
|
|
8
|
+
*
|
|
9
|
+
* The Winston path applies these features inside the wrapped log method
|
|
10
|
+
* (see lib/logger.js -> wrapLoggerMethod). Pino has no such wrapper, but it does expose
|
|
11
|
+
* `hooks.logMethod(inputArgs, method, level)` which is invoked *before* the log line is
|
|
12
|
+
* serialized. That is the correct place to drop, pass through, or rewrite a log call.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* const { createPinoFeatures } = require('./lib/pinoHooks') // internal module, wired up by pino.js createLogger();
|
|
16
|
+
* const features = createPinoFeatures({ sampling, rateLimit, deduplication });
|
|
17
|
+
* const logger = pino(features.enabled ? { hooks: { logMethod: features.logMethod } } : {});
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const { LogSampler } = require('./logSampler');
|
|
21
|
+
const { LogDeduplicator } = require('./logDeduplicator');
|
|
22
|
+
const NTL_LEVELS = require('./levels');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Pino's standard numeric levels mapped to NightTimeLogger level names.
|
|
26
|
+
* NTL has no numeric equivalent for `internal`; custom levels are resolved by name
|
|
27
|
+
* through the `levels` option (pino's `logger.levels.values`).
|
|
28
|
+
* @type {Readonly<Object<number, string>>}
|
|
29
|
+
*/
|
|
30
|
+
const PINO_LEVEL_TO_NTL = Object.freeze({
|
|
31
|
+
10: 'trace',
|
|
32
|
+
20: 'debug',
|
|
33
|
+
30: 'info',
|
|
34
|
+
40: 'warn',
|
|
35
|
+
50: 'error',
|
|
36
|
+
60: 'fatal',
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
/** Maximum number of interpolation arguments folded into a dedup fingerprint. */
|
|
40
|
+
const MAX_FINGERPRINT_ARGS = 8;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check whether a value is a plain-ish object (merge object candidate).
|
|
44
|
+
* @param {*} value - Value to test
|
|
45
|
+
* @returns {boolean} - True when the value can act as a Pino merge object
|
|
46
|
+
*/
|
|
47
|
+
function isMergeObject(value) {
|
|
48
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Error);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Normalize the `sampling` option into a plain level -> rate map.
|
|
53
|
+
* @param {object} sampling - Raw sampling config
|
|
54
|
+
* @returns {object} - Sanitized sampling map
|
|
55
|
+
*/
|
|
56
|
+
function normalizeSampling(sampling) {
|
|
57
|
+
const out = {};
|
|
58
|
+
if (!sampling || typeof sampling !== 'object') {
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
for (const [level, rate] of Object.entries(sampling)) {
|
|
62
|
+
if (typeof rate === 'number' && Number.isFinite(rate)) {
|
|
63
|
+
out[level] = rate;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Normalize the `rateLimit` option into a plain level -> { max, window } map.
|
|
71
|
+
* Accepts `window` (documented) or `windowMs` (alias).
|
|
72
|
+
* @param {object} rateLimit - Raw rate limit config
|
|
73
|
+
* @returns {object} - Sanitized rate limit map
|
|
74
|
+
*/
|
|
75
|
+
function normalizeRateLimit(rateLimit) {
|
|
76
|
+
const out = {};
|
|
77
|
+
if (!rateLimit || typeof rateLimit !== 'object') {
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
for (const [level, limit] of Object.entries(rateLimit)) {
|
|
81
|
+
if (!limit || typeof limit !== 'object') {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const max = typeof limit.max === 'number' ? limit.max : null;
|
|
85
|
+
const window = typeof limit.window === 'number'
|
|
86
|
+
? limit.window
|
|
87
|
+
: (typeof limit.windowMs === 'number' ? limit.windowMs : 60000);
|
|
88
|
+
if (max === null || !Number.isFinite(max) || !Number.isFinite(window)) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
out[level] = { max, window };
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Normalize the `deduplication` option.
|
|
98
|
+
* Accepts `true`, or an object with `{ enabled, threshold, window | windowMs, levels }`.
|
|
99
|
+
* @param {object|boolean} deduplication - Raw deduplication config
|
|
100
|
+
* @returns {{enabled: boolean, threshold: number, window: number, levels: (string[]|null)}} - Sanitized config
|
|
101
|
+
*/
|
|
102
|
+
function normalizeDeduplication(deduplication) {
|
|
103
|
+
const disabled = { enabled: false, threshold: 3, window: 60000, levels: null };
|
|
104
|
+
|
|
105
|
+
if (deduplication === true) {
|
|
106
|
+
return { enabled: true, threshold: 3, window: 60000, levels: null };
|
|
107
|
+
}
|
|
108
|
+
if (!deduplication || typeof deduplication !== 'object') {
|
|
109
|
+
return disabled;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const keys = Object.keys(deduplication);
|
|
113
|
+
if (keys.length === 0) {
|
|
114
|
+
return disabled;
|
|
115
|
+
}
|
|
116
|
+
if (deduplication.enabled === false) {
|
|
117
|
+
return disabled;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const window = typeof deduplication.window === 'number'
|
|
121
|
+
? deduplication.window
|
|
122
|
+
: (typeof deduplication.windowMs === 'number' ? deduplication.windowMs : 60000);
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
enabled: true,
|
|
126
|
+
threshold: typeof deduplication.threshold === 'number' ? deduplication.threshold : 3,
|
|
127
|
+
window,
|
|
128
|
+
levels: Array.isArray(deduplication.levels) ? deduplication.levels.slice() : null,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Build a numeric -> NTL level name resolver.
|
|
134
|
+
* Custom levels are resolved through the supplied pino `levels` map (name -> number);
|
|
135
|
+
* unresolvable levels return null, which means "pass through unfiltered".
|
|
136
|
+
* @param {object} levels - Pino `logger.levels.values` map (name -> number)
|
|
137
|
+
* @returns {function(number): (string|null)} - Resolver function
|
|
138
|
+
*/
|
|
139
|
+
function createLevelResolver(levels) {
|
|
140
|
+
const byNumber = {};
|
|
141
|
+
|
|
142
|
+
if (levels && typeof levels === 'object') {
|
|
143
|
+
for (const [name, value] of Object.entries(levels)) {
|
|
144
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
145
|
+
byNumber[value] = name;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return function resolveLevel(levelValue) {
|
|
151
|
+
if (typeof levelValue === 'string') {
|
|
152
|
+
return levelValue;
|
|
153
|
+
}
|
|
154
|
+
if (typeof levelValue !== 'number' || !Number.isFinite(levelValue)) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
const custom = byNumber[levelValue];
|
|
158
|
+
if (typeof custom === 'string') {
|
|
159
|
+
return custom;
|
|
160
|
+
}
|
|
161
|
+
const standard = PINO_LEVEL_TO_NTL[levelValue];
|
|
162
|
+
return typeof standard === 'string' ? standard : null;
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Locate the message argument inside the arguments Pino handed to `logMethod`.
|
|
168
|
+
*
|
|
169
|
+
* Supported shapes:
|
|
170
|
+
* (msg) -> index 0
|
|
171
|
+
* (msg, ...interpolationArgs) -> index 0
|
|
172
|
+
* (mergeObject, msg, ...args) -> index 1
|
|
173
|
+
* (error) -> message from error, insert rewrite at index 1
|
|
174
|
+
* (mergeObject) -> no message
|
|
175
|
+
*
|
|
176
|
+
* @param {Array} inputArgs - Arguments Pino passed to the hook
|
|
177
|
+
* @returns {{message: (string|null), index: number, insert: boolean, location: (string|null)}} - Extraction result
|
|
178
|
+
*/
|
|
179
|
+
function extractMessage(inputArgs) {
|
|
180
|
+
const result = { message: null, index: -1, insert: false, location: null };
|
|
181
|
+
|
|
182
|
+
if (!inputArgs || inputArgs.length === 0) {
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const first = inputArgs[0];
|
|
187
|
+
|
|
188
|
+
if (typeof first === 'string') {
|
|
189
|
+
result.message = first;
|
|
190
|
+
result.index = 0;
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (first instanceof Error) {
|
|
195
|
+
// logger.error(err) -> pino uses err.message as msg. Rewriting means adding an
|
|
196
|
+
// explicit msg argument, i.e. logger.error(err, 'boom (x3)').
|
|
197
|
+
if (typeof inputArgs[1] === 'string') {
|
|
198
|
+
result.message = inputArgs[1];
|
|
199
|
+
result.index = 1;
|
|
200
|
+
} else if (typeof first.message === 'string') {
|
|
201
|
+
result.message = first.message;
|
|
202
|
+
result.index = 1;
|
|
203
|
+
result.insert = true;
|
|
204
|
+
}
|
|
205
|
+
return result;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (isMergeObject(first)) {
|
|
209
|
+
if (typeof first.module === 'string') {
|
|
210
|
+
result.location = first.module;
|
|
211
|
+
}
|
|
212
|
+
if (typeof inputArgs[1] === 'string') {
|
|
213
|
+
result.message = inputArgs[1];
|
|
214
|
+
result.index = 1;
|
|
215
|
+
}
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Build the string used to fingerprint a log call. Primitive interpolation arguments are
|
|
224
|
+
* folded in so that `info('user %s in', 'a')` and `info('user %s in', 'b')` stay distinct,
|
|
225
|
+
* while lib/messageNormalizer.js still collapses varying ids/uuids/numbers.
|
|
226
|
+
* Object arguments are reduced to a constant placeholder so nothing is retained.
|
|
227
|
+
* @param {string} message - The message string
|
|
228
|
+
* @param {Array} inputArgs - Arguments Pino passed to the hook
|
|
229
|
+
* @param {number} messageIndex - Index of the message argument (or insert position)
|
|
230
|
+
* @returns {string} - Fingerprint source string
|
|
231
|
+
*/
|
|
232
|
+
function buildFingerprintSource(message, inputArgs, messageIndex) {
|
|
233
|
+
let source = message;
|
|
234
|
+
const start = messageIndex + 1;
|
|
235
|
+
const end = Math.min(inputArgs.length, start + MAX_FINGERPRINT_ARGS);
|
|
236
|
+
|
|
237
|
+
for (let i = start; i < end; i++) {
|
|
238
|
+
const arg = inputArgs[i];
|
|
239
|
+
const type = typeof arg;
|
|
240
|
+
if (type === 'string' || type === 'number' || type === 'boolean' || type === 'bigint') {
|
|
241
|
+
source += ` ${String(arg)}`;
|
|
242
|
+
} else if (arg === null || arg === undefined) {
|
|
243
|
+
source += ' null';
|
|
244
|
+
} else {
|
|
245
|
+
source += ' [obj]';
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return source;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Create the Pino feature adapter.
|
|
254
|
+
*
|
|
255
|
+
* @param {object} [options] - Options
|
|
256
|
+
* @param {object} [options.sampling] - Per-level sampling rates, e.g. { debug: 0.1, info: 0.5 }
|
|
257
|
+
* @param {object} [options.rateLimit] - Per-level rate limits, e.g. { info: { max: 100, window: 60000 } }
|
|
258
|
+
* @param {object|boolean} [options.deduplication] - { enabled, threshold, window|windowMs, levels } or true
|
|
259
|
+
* @param {object} [options.levels] - Pino `logger.levels.values` map (name -> number)
|
|
260
|
+
* @param {string} [options.defaultLocation] - Fallback dedup location (mixin output is not in inputArgs)
|
|
261
|
+
* @param {object} [options.sampler] - Pre-built sampler (primarily for testing)
|
|
262
|
+
* @param {object} [options.deduplicator] - Pre-built deduplicator (primarily for testing)
|
|
263
|
+
* @returns {object} - { enabled, logMethod, getStats, resetStats, destroy, sampler, deduplicator }
|
|
264
|
+
*/
|
|
265
|
+
function createPinoFeatures(options = {}) {
|
|
266
|
+
const opts = options || {};
|
|
267
|
+
|
|
268
|
+
const sampling = normalizeSampling(opts.sampling);
|
|
269
|
+
const rateLimit = normalizeRateLimit(opts.rateLimit);
|
|
270
|
+
const dedupConfig = normalizeDeduplication(opts.deduplication);
|
|
271
|
+
|
|
272
|
+
const hasSamplerConfig = Object.keys(sampling).length > 0 || Object.keys(rateLimit).length > 0;
|
|
273
|
+
|
|
274
|
+
const sampler = opts.sampler !== undefined && opts.sampler !== null
|
|
275
|
+
? opts.sampler
|
|
276
|
+
: (hasSamplerConfig ? new LogSampler({ sampling, rateLimit }) : null);
|
|
277
|
+
|
|
278
|
+
const deduplicator = opts.deduplicator !== undefined && opts.deduplicator !== null
|
|
279
|
+
? opts.deduplicator
|
|
280
|
+
: (dedupConfig.enabled
|
|
281
|
+
? new LogDeduplicator({
|
|
282
|
+
enabled: true,
|
|
283
|
+
threshold: dedupConfig.threshold,
|
|
284
|
+
window: dedupConfig.window,
|
|
285
|
+
})
|
|
286
|
+
: null);
|
|
287
|
+
|
|
288
|
+
const defaultLocation = typeof opts.defaultLocation === 'string' ? opts.defaultLocation : null;
|
|
289
|
+
const resolveLevel = createLevelResolver(opts.levels);
|
|
290
|
+
|
|
291
|
+
// `fatal` is never dropped unless the configuration explicitly opts it in.
|
|
292
|
+
const fatalSamplerExplicit = Object.prototype.hasOwnProperty.call(sampling, 'fatal')
|
|
293
|
+
|| Object.prototype.hasOwnProperty.call(rateLimit, 'fatal');
|
|
294
|
+
// Opting fatal in requires either listing it in `deduplication.levels` or `deduplication.fatal: true`.
|
|
295
|
+
const fatalDedupExplicit = (Array.isArray(dedupConfig.levels) && dedupConfig.levels.indexOf('fatal') !== -1)
|
|
296
|
+
|| Boolean(opts.deduplication && typeof opts.deduplication === 'object' && opts.deduplication.fatal === true);
|
|
297
|
+
|
|
298
|
+
const dedupLevels = dedupConfig.levels;
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Check whether deduplication applies to a level.
|
|
302
|
+
* @param {string} level - NTL level name
|
|
303
|
+
* @returns {boolean} - True when the level should be deduplicated
|
|
304
|
+
*/
|
|
305
|
+
function dedupAppliesTo(level) {
|
|
306
|
+
if (dedupLevels && dedupLevels.indexOf(level) === -1) {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
if (level === 'fatal' && !fatalDedupExplicit) {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Pino `hooks.logMethod` implementation.
|
|
317
|
+
* MUST be invoked with `this` bound to the pino logger instance (Pino does this).
|
|
318
|
+
* Never throws: any internal failure falls back to emitting the original log call.
|
|
319
|
+
*
|
|
320
|
+
* @param {Array} inputArgs - Arguments passed to the log method
|
|
321
|
+
* @param {Function} method - The underlying pino log method
|
|
322
|
+
* @param {number} level - Numeric pino level
|
|
323
|
+
* @returns {*} - Result of the underlying log method, or undefined when dropped
|
|
324
|
+
*/
|
|
325
|
+
function logMethod(inputArgs, method, level) {
|
|
326
|
+
let emitted = false;
|
|
327
|
+
|
|
328
|
+
try {
|
|
329
|
+
const ntlLevel = resolveLevel(level);
|
|
330
|
+
|
|
331
|
+
// Unknown/custom level that cannot be resolved: pass through unfiltered.
|
|
332
|
+
if (ntlLevel === null) {
|
|
333
|
+
emitted = true;
|
|
334
|
+
return method.apply(this, inputArgs);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const isFatal = ntlLevel === 'fatal';
|
|
338
|
+
|
|
339
|
+
// Rate limiting then sampling (LogSampler#shouldProcess applies them in that order).
|
|
340
|
+
if (sampler && (!isFatal || fatalSamplerExplicit)) {
|
|
341
|
+
if (sampler.shouldProcess(ntlLevel) === false) {
|
|
342
|
+
return undefined;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Deduplication.
|
|
347
|
+
if (deduplicator && dedupAppliesTo(ntlLevel)) {
|
|
348
|
+
const extracted = extractMessage(inputArgs);
|
|
349
|
+
if (extracted.message !== null) {
|
|
350
|
+
const fingerprintSource = buildFingerprintSource(
|
|
351
|
+
extracted.message,
|
|
352
|
+
inputArgs,
|
|
353
|
+
extracted.insert ? extracted.index - 1 : extracted.index
|
|
354
|
+
);
|
|
355
|
+
// Meta is intentionally a fresh, tiny object: LogDeduplicator retains it as
|
|
356
|
+
// part of its entry sample, so no logged payload may be referenced here.
|
|
357
|
+
const decision = deduplicator.check(ntlLevel, fingerprintSource, {
|
|
358
|
+
location: extracted.location || defaultLocation || undefined,
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
if (decision) {
|
|
362
|
+
if (decision.shouldLog === false) {
|
|
363
|
+
return undefined;
|
|
364
|
+
}
|
|
365
|
+
if (decision.count > 1 && decision.message !== fingerprintSource) {
|
|
366
|
+
const suffix = decision.message.slice(fingerprintSource.length);
|
|
367
|
+
if (suffix) {
|
|
368
|
+
const newArgs = inputArgs.slice();
|
|
369
|
+
if (extracted.insert) {
|
|
370
|
+
newArgs.splice(extracted.index, 0, extracted.message + suffix);
|
|
371
|
+
} else {
|
|
372
|
+
newArgs[extracted.index] = extracted.message + suffix;
|
|
373
|
+
}
|
|
374
|
+
emitted = true;
|
|
375
|
+
return method.apply(this, newArgs);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
emitted = true;
|
|
383
|
+
return method.apply(this, inputArgs);
|
|
384
|
+
} catch (err) {
|
|
385
|
+
if (emitted) {
|
|
386
|
+
// The failure came from the log call itself; behave as if no hook was installed.
|
|
387
|
+
throw err;
|
|
388
|
+
}
|
|
389
|
+
// A bug in the feature pipeline must never lose a log line.
|
|
390
|
+
return method.apply(this, inputArgs);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return {
|
|
395
|
+
enabled: Boolean(sampler || deduplicator),
|
|
396
|
+
sampler,
|
|
397
|
+
deduplicator,
|
|
398
|
+
logMethod,
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Get feature statistics.
|
|
402
|
+
* @returns {{sampling: (object|null), deduplication: (object|null)}} - Stats snapshot
|
|
403
|
+
*/
|
|
404
|
+
getStats() {
|
|
405
|
+
return {
|
|
406
|
+
sampling: sampler && typeof sampler.getStats === 'function' ? sampler.getStats() : null,
|
|
407
|
+
deduplication: deduplicator && typeof deduplicator.getStats === 'function'
|
|
408
|
+
? deduplicator.getStats()
|
|
409
|
+
: null,
|
|
410
|
+
};
|
|
411
|
+
},
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Reset feature statistics.
|
|
415
|
+
* @returns {void}
|
|
416
|
+
*/
|
|
417
|
+
resetStats() {
|
|
418
|
+
if (sampler && typeof sampler.resetStats === 'function') {
|
|
419
|
+
sampler.resetStats();
|
|
420
|
+
}
|
|
421
|
+
if (deduplicator && typeof deduplicator.resetStats === 'function') {
|
|
422
|
+
deduplicator.resetStats();
|
|
423
|
+
}
|
|
424
|
+
},
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Release timers and internal state so the process can exit cleanly.
|
|
428
|
+
* @returns {void}
|
|
429
|
+
*/
|
|
430
|
+
destroy() {
|
|
431
|
+
if (sampler && typeof sampler.destroy === 'function') {
|
|
432
|
+
sampler.destroy();
|
|
433
|
+
}
|
|
434
|
+
if (deduplicator && typeof deduplicator.destroy === 'function') {
|
|
435
|
+
deduplicator.destroy();
|
|
436
|
+
}
|
|
437
|
+
},
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
module.exports = {
|
|
442
|
+
createPinoFeatures,
|
|
443
|
+
PINO_LEVEL_TO_NTL,
|
|
444
|
+
extractMessage,
|
|
445
|
+
normalizeDeduplication,
|
|
446
|
+
};
|