infront-logger 1.1.16 → 1.1.18

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 CHANGED
@@ -135,7 +135,12 @@ Make sure to use before defining schemas;
135
135
 
136
136
  const {MongooseLoggerPlugin} = require('infront-logger');
137
137
  const mongoose = require('mongoose');
138
- let plugin = MongooseLoggerPlugin({console : false, filename : 'db.log'});
138
+ let plugin = MongooseLoggerPlugin({
139
+ console: false,
140
+ filename: 'db.log',
141
+ sampleRate: 0.1, // Sample 10% of DB queries
142
+ throttleMs: 1000 // Throttle duplicate queries to once per second
143
+ });
139
144
  mongoose.plugin(plugin);
140
145
 
141
146
  ## Loggers
@@ -197,3 +202,72 @@ All methods support both string messages and context objects. Context objects ar
197
202
  - errorFilename - name of the default error log file (default: "error.log")
198
203
  - console - boolean - should log to console (default: true)
199
204
  - consoleJSON - boolean - print full json to console (default : false)
205
+ - sampleRate - number (0.0 to 1.0) - Percentage of logs to sample. 1.0 = log everything, 0.5 = log 50%, etc. Applies to all enabled transports (console and/or file) (default: 1.0)
206
+ - bypassSamplingForErrors - boolean - If true, error logs bypass sampling and are always logged regardless of sampleRate settings (default: true)
207
+ - throttleMs - number - Throttle logs by milliseconds. Prevents the same log message from being logged more than once within the specified time window. 0 = no throttling (default: 0)
208
+
209
+ ## Advanced Features
210
+
211
+ ### Log Sampling
212
+
213
+ Control the volume of logs by using sampling rates. This is useful in high-traffic scenarios where you want to reduce log volume while maintaining visibility.
214
+
215
+ ```javascript
216
+ // Sample 50% of logs
217
+ const logger = new BaseLogger("myComponent", {
218
+ sampleRate: 0.5 // 50% of logs will be written
219
+ });
220
+
221
+ // Sample 10% of logs
222
+ const logger = new BaseLogger("myComponent", {
223
+ sampleRate: 0.1 // 10% of logs will be written
224
+ });
225
+ ```
226
+
227
+ **Note**: The sample rate applies to all enabled transports (console and/or file). When both console and file are enabled, Winston logs to all enabled transports in a single call, so the same sample rate applies to both.
228
+
229
+ ### Error Log Bypass
230
+
231
+ By default, error logs bypass sampling to ensure critical errors are always logged. You can disable this behavior:
232
+
233
+ ```javascript
234
+ // Errors will also be subject to sampling
235
+ const logger = new BaseLogger("myComponent", {
236
+ sampleRate: 0.1,
237
+ bypassSamplingForErrors: false // Errors will also be sampled
238
+ });
239
+ ```
240
+
241
+ ### Log Throttling
242
+
243
+ Throttle prevents duplicate log messages from flooding your logs. The same message (same level + same content) can only be logged once within the throttle window.
244
+
245
+ ```javascript
246
+ // Throttle logs to once per 5 seconds
247
+ const logger = new BaseLogger("myComponent", {
248
+ throttleMs: 5000 // Same message can only log once every 5 seconds
249
+ });
250
+
251
+ // Throttle to once per minute
252
+ const logger = new BaseLogger("myComponent", {
253
+ throttleMs: 60000 // Same message can only log once per minute
254
+ });
255
+ ```
256
+
257
+ **How it works**:
258
+ - First occurrence of a message is always logged
259
+ - Subsequent identical messages within the throttle window are suppressed
260
+ - After the throttle window expires, the message can be logged again
261
+ - The throttle cache is automatically cleaned up to prevent memory leaks
262
+
263
+ ### Combining Features
264
+
265
+ You can combine sampling, throttling, and error bypass:
266
+
267
+ ```javascript
268
+ const logger = new BaseLogger("myComponent", {
269
+ sampleRate: 0.1, // 10% sampling
270
+ bypassSamplingForErrors: true, // Always log errors (default)
271
+ throttleMs: 5000 // Throttle duplicate messages to once per 5 seconds
272
+ });
273
+ ```
@@ -3279,8 +3279,7 @@ var __decorate = function(decorators, target, key, desc) {
3279
3279
  };
3280
3280
  var HandlerType = {
3281
3281
  console: "console",
3282
- http: "http",
3283
- silent: "silent"
3282
+ http: "http"
3284
3283
  };
3285
3284
  var STATUSES = Object.keys(StatusType);
3286
3285
  var Logger = (
@@ -3891,8 +3890,7 @@ function truncateResponseStream(stream, bytesLimit, callback) {
3891
3890
  callback(void 0, responseText);
3892
3891
  }
3893
3892
  }, {
3894
- bytesLimit,
3895
- collectStreamBody: true
3893
+ bytesLimit
3896
3894
  });
3897
3895
  }
3898
3896
  function startRuntimeErrorCollection(configuration, lifeCycle) {
@@ -3283,8 +3283,7 @@
3283
3283
  };
3284
3284
  var HandlerType = {
3285
3285
  console: "console",
3286
- http: "http",
3287
- silent: "silent"
3286
+ http: "http"
3288
3287
  };
3289
3288
  var STATUSES = Object.keys(StatusType);
3290
3289
  var Logger = (
@@ -3895,8 +3894,7 @@
3895
3894
  callback(void 0, responseText);
3896
3895
  }
3897
3896
  }, {
3898
- bytesLimit,
3899
- collectStreamBody: true
3897
+ bytesLimit
3900
3898
  });
3901
3899
  }
3902
3900
  function startRuntimeErrorCollection(configuration, lifeCycle) {
package/dist/index.es.js CHANGED
@@ -1,6 +1,6 @@
1
1
  require("winston-daily-rotate-file");
2
2
  const { format: format$1, createLogger, transports, addColors } = require("winston");
3
- require("util");
3
+ const { inspect } = require("util");
4
4
  const colors = {
5
5
  error: "red",
6
6
  warn: "yellow",
@@ -186,8 +186,7 @@ function bytesToMB$1(b) {
186
186
  return toFixedNumber(b / 1024 / 1024, 2, 10);
187
187
  }
188
188
  function formatBytes$2(bytes, decimals = 2) {
189
- if (!+bytes)
190
- return "0 Bytes";
189
+ if (!+bytes) return "0 Bytes";
191
190
  const k = 1024;
192
191
  const dm = decimals < 0 ? 0 : decimals;
193
192
  const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
@@ -237,6 +236,65 @@ function stats() {
237
236
  return {};
238
237
  }
239
238
  }
239
+ class Throttle {
240
+ constructor(throttleMs) {
241
+ this.throttleMs = throttleMs || 0;
242
+ this._throttleCache = /* @__PURE__ */ new Map();
243
+ if (this.throttleMs > 0) {
244
+ this._cleanupInterval = setInterval(() => {
245
+ this.cleanup();
246
+ }, Math.max(this.throttleMs * 2, 6e4));
247
+ }
248
+ }
249
+ _getThrottleKey(level, ...args) {
250
+ const messageParts = Array.from(args).map((arg) => {
251
+ if (typeof arg === "object" && arg !== null) {
252
+ try {
253
+ return JSON.stringify(arg);
254
+ } catch (e) {
255
+ return String(arg);
256
+ }
257
+ }
258
+ return String(arg);
259
+ });
260
+ return `${level}:${messageParts.join("|")}`;
261
+ }
262
+ isThrottled(level, ...args) {
263
+ if (this.throttleMs <= 0) {
264
+ return false;
265
+ }
266
+ const key = this._getThrottleKey(level, ...args);
267
+ const now = Date.now();
268
+ const lastLogTime = this._throttleCache.get(key);
269
+ if (lastLogTime === void 0) {
270
+ this._throttleCache.set(key, now);
271
+ return false;
272
+ }
273
+ const timeSinceLastLog = now - lastLogTime;
274
+ if (timeSinceLastLog >= this.throttleMs) {
275
+ this._throttleCache.set(key, now);
276
+ return false;
277
+ }
278
+ return true;
279
+ }
280
+ cleanup() {
281
+ if (this.throttleMs <= 0) return;
282
+ const now = Date.now();
283
+ const cutoffTime = now - this.throttleMs * 2;
284
+ for (const [key, timestamp] of this._throttleCache.entries()) {
285
+ if (timestamp < cutoffTime) {
286
+ this._throttleCache.delete(key);
287
+ }
288
+ }
289
+ }
290
+ destroy() {
291
+ if (this._cleanupInterval) {
292
+ clearInterval(this._cleanupInterval);
293
+ this._cleanupInterval = null;
294
+ }
295
+ this._throttleCache.clear();
296
+ }
297
+ }
240
298
  class BaseLogger {
241
299
  constructor(component, options) {
242
300
  this.options = options;
@@ -245,6 +303,9 @@ class BaseLogger {
245
303
  this.startTime = Date.now();
246
304
  this.absaluteStartTime = Date.now();
247
305
  this.excludePatterns = [];
306
+ this.sampleRate = options.sampleRate ?? 1;
307
+ this.bypassSamplingForErrors = options.bypassSamplingForErrors !== false;
308
+ this.throttle = new Throttle(options.throttleMs || 0);
248
309
  }
249
310
  session(id) {
250
311
  this.ctx.sessionID = id;
@@ -272,10 +333,19 @@ class BaseLogger {
272
333
  return false;
273
334
  });
274
335
  }
336
+ _shouldLog() {
337
+ return this.sampleRate >= 1 || Math.random() < this.sampleRate;
338
+ }
275
339
  log() {
276
340
  if (arguments.length > 0 && this._shouldExclude(arguments[0])) {
277
341
  return;
278
342
  }
343
+ if (this.throttle.isThrottled("info", ...arguments)) {
344
+ return;
345
+ }
346
+ if (!this._shouldLog()) {
347
+ return;
348
+ }
279
349
  this.logger.info(...arguments, this.ctx);
280
350
  this._stopMemProfile();
281
351
  }
@@ -283,6 +353,12 @@ class BaseLogger {
283
353
  if (arguments.length > 0 && this._shouldExclude(arguments[0])) {
284
354
  return;
285
355
  }
356
+ if (this.throttle.isThrottled("info", ...arguments)) {
357
+ return;
358
+ }
359
+ if (!this._shouldLog()) {
360
+ return;
361
+ }
286
362
  this.logger.info(...arguments, this.ctx);
287
363
  this._stopMemProfile();
288
364
  }
@@ -290,6 +366,12 @@ class BaseLogger {
290
366
  if (arguments.length > 0 && this._shouldExclude(arguments[0])) {
291
367
  return;
292
368
  }
369
+ if (this.throttle.isThrottled("error", ...arguments)) {
370
+ return;
371
+ }
372
+ if (!this.bypassSamplingForErrors && !this._shouldLog()) {
373
+ return;
374
+ }
293
375
  this.logger.error(...arguments, this.ctx);
294
376
  this._stopMemProfile();
295
377
  }
@@ -297,6 +379,12 @@ class BaseLogger {
297
379
  if (arguments.length > 0 && this._shouldExclude(arguments[0])) {
298
380
  return;
299
381
  }
382
+ if (this.throttle.isThrottled("warn", ...arguments)) {
383
+ return;
384
+ }
385
+ if (!this._shouldLog()) {
386
+ return;
387
+ }
300
388
  this.logger.warn(...arguments, this.ctx);
301
389
  this._stopMemProfile();
302
390
  }
@@ -304,6 +392,12 @@ class BaseLogger {
304
392
  if (arguments.length > 0 && this._shouldExclude(arguments[0])) {
305
393
  return;
306
394
  }
395
+ if (this.throttle.isThrottled("debug", ...arguments)) {
396
+ return;
397
+ }
398
+ if (!this._shouldLog()) {
399
+ return;
400
+ }
307
401
  this.logger.debug(...arguments, this.ctx);
308
402
  this._stopMemProfile();
309
403
  }
@@ -320,6 +414,12 @@ class BaseLogger {
320
414
  _stopMemProfile() {
321
415
  if (this.memProfileInterval) clearInterval(this.memProfileInterval);
322
416
  }
417
+ _cleanup() {
418
+ this._stopMemProfile();
419
+ if (this.throttle) {
420
+ this.throttle.destroy();
421
+ }
422
+ }
323
423
  profileMem(options = {}) {
324
424
  this.ctx.maxMemory = this.ctx.maxMemory || 0;
325
425
  this.ctx.memoryStats = this.ctx.memoryStats || [];
package/dist/index.umd.js CHANGED
@@ -4,7 +4,7 @@
4
4
  "use strict";
5
5
  require("winston-daily-rotate-file");
6
6
  const { format: format$1, createLogger, transports, addColors } = require("winston");
7
- require("util");
7
+ const { inspect } = require("util");
8
8
  const colors = {
9
9
  error: "red",
10
10
  warn: "yellow",
@@ -190,8 +190,7 @@
190
190
  return toFixedNumber(b / 1024 / 1024, 2, 10);
191
191
  }
192
192
  function formatBytes$2(bytes, decimals = 2) {
193
- if (!+bytes)
194
- return "0 Bytes";
193
+ if (!+bytes) return "0 Bytes";
195
194
  const k = 1024;
196
195
  const dm = decimals < 0 ? 0 : decimals;
197
196
  const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
@@ -241,6 +240,65 @@
241
240
  return {};
242
241
  }
243
242
  }
243
+ class Throttle {
244
+ constructor(throttleMs) {
245
+ this.throttleMs = throttleMs || 0;
246
+ this._throttleCache = /* @__PURE__ */ new Map();
247
+ if (this.throttleMs > 0) {
248
+ this._cleanupInterval = setInterval(() => {
249
+ this.cleanup();
250
+ }, Math.max(this.throttleMs * 2, 6e4));
251
+ }
252
+ }
253
+ _getThrottleKey(level, ...args) {
254
+ const messageParts = Array.from(args).map((arg) => {
255
+ if (typeof arg === "object" && arg !== null) {
256
+ try {
257
+ return JSON.stringify(arg);
258
+ } catch (e) {
259
+ return String(arg);
260
+ }
261
+ }
262
+ return String(arg);
263
+ });
264
+ return `${level}:${messageParts.join("|")}`;
265
+ }
266
+ isThrottled(level, ...args) {
267
+ if (this.throttleMs <= 0) {
268
+ return false;
269
+ }
270
+ const key = this._getThrottleKey(level, ...args);
271
+ const now = Date.now();
272
+ const lastLogTime = this._throttleCache.get(key);
273
+ if (lastLogTime === void 0) {
274
+ this._throttleCache.set(key, now);
275
+ return false;
276
+ }
277
+ const timeSinceLastLog = now - lastLogTime;
278
+ if (timeSinceLastLog >= this.throttleMs) {
279
+ this._throttleCache.set(key, now);
280
+ return false;
281
+ }
282
+ return true;
283
+ }
284
+ cleanup() {
285
+ if (this.throttleMs <= 0) return;
286
+ const now = Date.now();
287
+ const cutoffTime = now - this.throttleMs * 2;
288
+ for (const [key, timestamp] of this._throttleCache.entries()) {
289
+ if (timestamp < cutoffTime) {
290
+ this._throttleCache.delete(key);
291
+ }
292
+ }
293
+ }
294
+ destroy() {
295
+ if (this._cleanupInterval) {
296
+ clearInterval(this._cleanupInterval);
297
+ this._cleanupInterval = null;
298
+ }
299
+ this._throttleCache.clear();
300
+ }
301
+ }
244
302
  class BaseLogger {
245
303
  constructor(component, options) {
246
304
  this.options = options;
@@ -249,6 +307,9 @@
249
307
  this.startTime = Date.now();
250
308
  this.absaluteStartTime = Date.now();
251
309
  this.excludePatterns = [];
310
+ this.sampleRate = options.sampleRate ?? 1;
311
+ this.bypassSamplingForErrors = options.bypassSamplingForErrors !== false;
312
+ this.throttle = new Throttle(options.throttleMs || 0);
252
313
  }
253
314
  session(id) {
254
315
  this.ctx.sessionID = id;
@@ -276,10 +337,19 @@
276
337
  return false;
277
338
  });
278
339
  }
340
+ _shouldLog() {
341
+ return this.sampleRate >= 1 || Math.random() < this.sampleRate;
342
+ }
279
343
  log() {
280
344
  if (arguments.length > 0 && this._shouldExclude(arguments[0])) {
281
345
  return;
282
346
  }
347
+ if (this.throttle.isThrottled("info", ...arguments)) {
348
+ return;
349
+ }
350
+ if (!this._shouldLog()) {
351
+ return;
352
+ }
283
353
  this.logger.info(...arguments, this.ctx);
284
354
  this._stopMemProfile();
285
355
  }
@@ -287,6 +357,12 @@
287
357
  if (arguments.length > 0 && this._shouldExclude(arguments[0])) {
288
358
  return;
289
359
  }
360
+ if (this.throttle.isThrottled("info", ...arguments)) {
361
+ return;
362
+ }
363
+ if (!this._shouldLog()) {
364
+ return;
365
+ }
290
366
  this.logger.info(...arguments, this.ctx);
291
367
  this._stopMemProfile();
292
368
  }
@@ -294,6 +370,12 @@
294
370
  if (arguments.length > 0 && this._shouldExclude(arguments[0])) {
295
371
  return;
296
372
  }
373
+ if (this.throttle.isThrottled("error", ...arguments)) {
374
+ return;
375
+ }
376
+ if (!this.bypassSamplingForErrors && !this._shouldLog()) {
377
+ return;
378
+ }
297
379
  this.logger.error(...arguments, this.ctx);
298
380
  this._stopMemProfile();
299
381
  }
@@ -301,6 +383,12 @@
301
383
  if (arguments.length > 0 && this._shouldExclude(arguments[0])) {
302
384
  return;
303
385
  }
386
+ if (this.throttle.isThrottled("warn", ...arguments)) {
387
+ return;
388
+ }
389
+ if (!this._shouldLog()) {
390
+ return;
391
+ }
304
392
  this.logger.warn(...arguments, this.ctx);
305
393
  this._stopMemProfile();
306
394
  }
@@ -308,6 +396,12 @@
308
396
  if (arguments.length > 0 && this._shouldExclude(arguments[0])) {
309
397
  return;
310
398
  }
399
+ if (this.throttle.isThrottled("debug", ...arguments)) {
400
+ return;
401
+ }
402
+ if (!this._shouldLog()) {
403
+ return;
404
+ }
311
405
  this.logger.debug(...arguments, this.ctx);
312
406
  this._stopMemProfile();
313
407
  }
@@ -324,6 +418,12 @@
324
418
  _stopMemProfile() {
325
419
  if (this.memProfileInterval) clearInterval(this.memProfileInterval);
326
420
  }
421
+ _cleanup() {
422
+ this._stopMemProfile();
423
+ if (this.throttle) {
424
+ this.throttle.destroy();
425
+ }
426
+ }
327
427
  profileMem(options = {}) {
328
428
  this.ctx.maxMemory = this.ctx.maxMemory || 0;
329
429
  this.ctx.memoryStats = this.ctx.memoryStats || [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "infront-logger",
3
- "version": "1.1.16",
3
+ "version": "1.1.18",
4
4
  "description": "",
5
5
  "files": [
6
6
  "dist"
@@ -18,6 +18,19 @@
18
18
  "require": "./dist/browser.umd.js"
19
19
  }
20
20
  },
21
+ "scripts": {
22
+ "test": "echo \"Error: no test specified\" && exit 1",
23
+ "version:patch": "npm version patch",
24
+ "dev": "vite",
25
+ "build:node": "vite build",
26
+ "build:node:watch": "vite build --watch",
27
+ "build:browser:watch": "vite build --config vite.config.browser.js --watch",
28
+ "build:watch": "concurrently \"npm run build:node:watch\" \"npm run build:browser:watch\"",
29
+ "preview": "vite preview",
30
+ "build:browser": " vite build --config vite.config.browser.js",
31
+ "build": "concurrently \"npm run build:node\" \"npm run build:browser\"",
32
+ "prebuild": "rimraf --glob dist/*"
33
+ },
21
34
  "keywords": [],
22
35
  "author": "",
23
36
  "license": "ISC",
@@ -31,21 +44,8 @@
31
44
  },
32
45
  "dependencies": {
33
46
  "@datadog/browser-logs": "~5.23.3",
47
+ "infront-utils": "~1.0.14",
34
48
  "winston": "^3.11.0",
35
- "winston-daily-rotate-file": "^4.7.1",
36
- "infront-utils": "^1.0.1"
37
- },
38
- "scripts": {
39
- "test": "echo \"Error: no test specified\" && exit 1",
40
- "version:patch": "npm version patch",
41
- "dev": "vite",
42
- "build:node": "vite build",
43
- "build:node:watch": "vite build --watch",
44
- "build:browser:watch": "vite build --config vite.config.browser.js --watch",
45
- "build:watch": "concurrently \"npm run build:node:watch\" \"npm run build:browser:watch\"",
46
- "preview": "vite preview",
47
- "build:browser": " vite build --config vite.config.browser.js",
48
- "build": "concurrently \"npm run build:node\" \"npm run build:browser\"",
49
- "prebuild": "rimraf --glob dist/*"
49
+ "winston-daily-rotate-file": "^4.7.1"
50
50
  }
51
- }
51
+ }