@naturalcycles/js-lib 15.90.0 → 15.92.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.
Files changed (38) hide show
  1. package/dist/asyncManager.js +3 -2
  2. package/dist/decorators/asyncMemo.decorator.d.ts +1 -1
  3. package/dist/decorators/asyncMemo.decorator.js +4 -3
  4. package/dist/decorators/logMethod.decorator.d.ts +1 -1
  5. package/dist/decorators/logMethod.decorator.js +8 -3
  6. package/dist/decorators/memo.decorator.d.ts +1 -1
  7. package/dist/decorators/memo.decorator.js +3 -2
  8. package/dist/decorators/memoSimple.decorator.js +8 -1
  9. package/dist/decorators/memoized.js +3 -2
  10. package/dist/decorators/memoizedAsync.js +4 -3
  11. package/dist/error/tryCatch.d.ts +1 -1
  12. package/dist/error/tryCatch.js +3 -2
  13. package/dist/http/fetcher.js +18 -17
  14. package/dist/http/fetcher.model.d.ts +1 -1
  15. package/dist/log/commonLogger.d.ts +40 -34
  16. package/dist/log/commonLogger.js +47 -92
  17. package/dist/promise/pGradualQueue.d.ts +1 -1
  18. package/dist/promise/pMap.d.ts +2 -2
  19. package/dist/promise/pMap.js +4 -3
  20. package/dist/promise/pQueue.d.ts +1 -1
  21. package/dist/promise/pRetry.d.ts +1 -1
  22. package/dist/promise/pRetry.js +3 -2
  23. package/package.json +1 -1
  24. package/src/asyncManager.ts +3 -2
  25. package/src/decorators/asyncMemo.decorator.ts +5 -4
  26. package/src/decorators/logMethod.decorator.ts +9 -4
  27. package/src/decorators/memo.decorator.ts +4 -3
  28. package/src/decorators/memoSimple.decorator.ts +2 -1
  29. package/src/decorators/memoized.ts +3 -2
  30. package/src/decorators/memoizedAsync.ts +4 -3
  31. package/src/error/tryCatch.ts +4 -3
  32. package/src/http/fetcher.model.ts +1 -1
  33. package/src/http/fetcher.ts +9 -10
  34. package/src/log/commonLogger.ts +80 -134
  35. package/src/promise/pGradualQueue.ts +1 -1
  36. package/src/promise/pMap.ts +10 -5
  37. package/src/promise/pQueue.ts +1 -1
  38. package/src/promise/pRetry.ts +4 -3
@@ -1,5 +1,6 @@
1
+ import { consoleLogger } from './log/commonLogger.js';
1
2
  class AsyncManagerImpl {
2
- logger = console;
3
+ logger = consoleLogger;
3
4
  pendingOps = new Set();
4
5
  onErrorHooks = [];
5
6
  runInBackground(promise) {
@@ -48,7 +49,7 @@ class AsyncManagerImpl {
48
49
  this.onErrorHooks.forEach(hook => hook(err));
49
50
  }
50
51
  else {
51
- this.logger.error('AsyncManager unhandled rejection:', err);
52
+ this.logger.error({ msg: 'AsyncManager unhandled rejection', err });
52
53
  }
53
54
  }
54
55
  }
@@ -13,7 +13,7 @@ export interface AsyncMemoOptions<FN> {
13
13
  */
14
14
  cacheKeyFn?: (args: MaybeParameters<FN>) => any;
15
15
  /**
16
- * Default to `console`
16
+ * Default to `consoleLogger`
17
17
  */
18
18
  logger?: CommonLogger;
19
19
  }
@@ -1,4 +1,5 @@
1
1
  import { _assert, _assertTypeOf } from '../error/assert.js';
2
+ import { consoleLogger } from '../log/commonLogger.js';
2
3
  import { _objectAssign, MISS } from '../types.js';
3
4
  import { _getTargetMethodSignature } from './decorator.util.js';
4
5
  import { jsonMemoSerializer } from './memo.util.js';
@@ -20,7 +21,7 @@ export const _AsyncMemo = (opt) => (target, key, descriptor) => {
20
21
  // It only holds values that are "in-flight", until Promise is resolved
21
22
  // After it's resolved - it's evicted from the cache and moved to the "proper" `instanceCache`
22
23
  const instancePromiseCache = new Map();
23
- const { logger = console, cacheFactory, cacheKeyFn = jsonMemoSerializer } = opt;
24
+ const { logger = consoleLogger, cacheFactory, cacheKeyFn = jsonMemoSerializer } = opt;
24
25
  const keyStr = String(key);
25
26
  const methodSignature = _getTargetMethodSignature(target, keyStr);
26
27
  // oxlint-disable-next-line @typescript-eslint/promise-function-async
@@ -56,7 +57,7 @@ export const _AsyncMemo = (opt) => (target, key, descriptor) => {
56
57
  return await onMiss();
57
58
  }, async (err) => {
58
59
  // Log the cache error and proceed "as cache Miss"
59
- logger.error(err);
60
+ logger.error({ err });
60
61
  return await onMiss();
61
62
  });
62
63
  promiseCache.set(cacheKey, promise);
@@ -73,7 +74,7 @@ export const _AsyncMemo = (opt) => (target, key, descriptor) => {
73
74
  await cache.set(cacheKey, value);
74
75
  }
75
76
  catch (err) {
76
- logger.error(err); // log and ignore the error
77
+ logger.error({ err }); // log and ignore the error
77
78
  }
78
79
  finally {
79
80
  // Clear the "in-flight" promise cache entry, as we now have a "permanent" cache entry
@@ -40,7 +40,7 @@ export interface LogMethodOptions {
40
40
  */
41
41
  logResultFn?: LogResultFn;
42
42
  /**
43
- * Defaults to `console`
43
+ * Defaults to `consoleLogger`
44
44
  */
45
45
  logger?: CommonLogger;
46
46
  }
@@ -1,5 +1,6 @@
1
1
  import { _ms } from '../datetime/time.util.js';
2
2
  import { _assert } from '../error/assert.js';
3
+ import { consoleLogger } from '../log/commonLogger.js';
3
4
  import { SimpleMovingAverage } from '../math/sma.js';
4
5
  import { _stringify } from '../string/stringify.js';
5
6
  import { _getArgsSignature, _getMethodSignature } from './decorator.util.js';
@@ -21,7 +22,7 @@ export function _LogMethod(opt = {}) {
21
22
  _assert(typeof descriptor.value === 'function', '@_LogMethod can be applied only to methods');
22
23
  const originalFn = descriptor.value;
23
24
  const keyStr = String(key);
24
- const { avg, logArgs = true, logStart, logResult, logResultLength = true, logger = console, } = opt;
25
+ const { avg, logArgs = true, logStart, logResult, logResultLength = true, logger = consoleLogger, } = opt;
25
26
  let { logResultFn } = opt;
26
27
  if (!logResultFn) {
27
28
  if (logResult) {
@@ -77,10 +78,14 @@ function logFinished(logger, callSignature, started, sma, logResultFn, res, err)
77
78
  t.push(`(avg ${_ms(sma.pushGetAvg(millis))})`);
78
79
  }
79
80
  if (err !== undefined) {
80
- t.push('ERROR:', err);
81
+ t.push('ERROR:');
81
82
  }
82
83
  else if (logResultFn) {
83
84
  t.push(...logResultFn(res));
84
85
  }
85
- logger.log(...t.filter(Boolean));
86
+ const msg = t
87
+ .filter(Boolean)
88
+ .map(part => _stringify(part))
89
+ .join(' ');
90
+ logger.log(err === undefined ? msg : { msg, err });
86
91
  }
@@ -14,7 +14,7 @@ export interface MemoOptions<FN> {
14
14
  */
15
15
  cacheKeyFn?: (args: MaybeParameters<FN>) => any;
16
16
  /**
17
- * Default to `console`
17
+ * Default to `consoleLogger`
18
18
  */
19
19
  logger?: CommonLogger;
20
20
  }
@@ -1,4 +1,5 @@
1
1
  import { _assert, _assertTypeOf } from '../error/assert.js';
2
+ import { consoleLogger } from '../log/commonLogger.js';
2
3
  import { _objectAssign } from '../types.js';
3
4
  import { _getTargetMethodSignature } from './decorator.util.js';
4
5
  import { jsonMemoSerializer, MapMemoCache } from './memo.util.js';
@@ -33,7 +34,7 @@ export const _Memo = (opt = {}) => (target, key, descriptor) => {
33
34
  // UPD: tests show that normal Map also doesn't leak (to be tested further)
34
35
  // Normal Map is needed to allow .clear()
35
36
  const instanceCache = new Map();
36
- const { logger = console, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, } = opt;
37
+ const { logger = consoleLogger, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, } = opt;
37
38
  const keyStr = String(key);
38
39
  const methodSignature = _getTargetMethodSignature(target, keyStr);
39
40
  descriptor.value = function (...args) {
@@ -54,7 +55,7 @@ export const _Memo = (opt = {}) => (target, key, descriptor) => {
54
55
  cache.set(cacheKey, value);
55
56
  }
56
57
  catch (err) {
57
- logger.error(err);
58
+ logger.error({ err });
58
59
  }
59
60
  return value;
60
61
  };
@@ -3,6 +3,13 @@
3
3
  // http://decodize.com/blog/2012/08/27/javascript-memoization-caching-results-for-better-performance/
4
4
  // http://inlehmansterms.net/2015/03/01/javascript-memoization/
5
5
  // https://community.risingstack.com/the-worlds-fastest-javascript-memoization-library/
6
+ /*
7
+ Optimized for 0 arguments (using SingleValueCache).
8
+ Optimized for 1 primitive argument (skips JSON.stringify).
9
+ Otherwise resorts to JSON.stringify.
10
+ Benchmark shows similar perf for ObjectCache and MapCache.
11
+ */
12
+ import { consoleLogger } from '../log/commonLogger.js';
6
13
  import { _getTargetMethodSignature } from './decorator.util.js';
7
14
  import { jsonMemoSerializer, MapMemoCache } from './memo.util.js';
8
15
  // memoSimple decorator is NOT exported. Only used in benchmarks currently
@@ -34,7 +41,7 @@ export const memoSimple = (opt = {}) => (target, key, descriptor) => {
34
41
  }
35
42
  */
36
43
  const cache = new MapMemoCache();
37
- const { logger = console } = opt;
44
+ const { logger = consoleLogger } = opt;
38
45
  const keyStr = String(key);
39
46
  const methodSignature = _getTargetMethodSignature(target, keyStr);
40
47
  descriptor.value = function (...args) {
@@ -1,3 +1,4 @@
1
+ import { consoleLogger } from '../log/commonLogger.js';
1
2
  import { jsonMemoSerializer, MapMemoCache } from './memo.util.js';
2
3
  /**
3
4
  * Only supports Sync functions.
@@ -5,7 +6,7 @@ import { jsonMemoSerializer, MapMemoCache } from './memo.util.js';
5
6
  * Technically, you can use it with Async functions, but it'll return the Promise without awaiting it.
6
7
  */
7
8
  export function _memoized(fn, opt = {}) {
8
- const { logger = console, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, } = opt;
9
+ const { logger = consoleLogger, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, } = opt;
9
10
  const cache = cacheFactory();
10
11
  const memoizedFn = function (...args) {
11
12
  const ctx = this;
@@ -18,7 +19,7 @@ export function _memoized(fn, opt = {}) {
18
19
  cache.set(cacheKey, value);
19
20
  }
20
21
  catch (err) {
21
- logger.error(err);
22
+ logger.error({ err });
22
23
  }
23
24
  return value;
24
25
  };
@@ -1,7 +1,8 @@
1
+ import { consoleLogger } from '../log/commonLogger.js';
1
2
  import { MISS } from '../types.js';
2
3
  import { jsonMemoSerializer } from './memo.util.js';
3
4
  export function _memoizedAsync(fn, opt) {
4
- const { logger = console, cacheFactory, cacheKeyFn = jsonMemoSerializer } = opt;
5
+ const { logger = consoleLogger, cacheFactory, cacheKeyFn = jsonMemoSerializer } = opt;
5
6
  const cache = cacheFactory();
6
7
  const memoizedFn = async function (...args) {
7
8
  const ctx = this;
@@ -11,7 +12,7 @@ export function _memoizedAsync(fn, opt) {
11
12
  value = await cache.get(cacheKey);
12
13
  }
13
14
  catch (err) {
14
- logger.error(err);
15
+ logger.error({ err });
15
16
  }
16
17
  if (value !== MISS) {
17
18
  return value;
@@ -22,7 +23,7 @@ export function _memoizedAsync(fn, opt) {
22
23
  await cache.set(cacheKey, value);
23
24
  }
24
25
  catch (err) {
25
- logger.error(err);
26
+ logger.error({ err });
26
27
  }
27
28
  })();
28
29
  return value;
@@ -15,7 +15,7 @@ export interface TryCatchOptions {
15
15
  */
16
16
  logError?: boolean;
17
17
  /**
18
- * Default to `console`
18
+ * Default to `consoleLogger`
19
19
  */
20
20
  logger?: CommonLogger;
21
21
  }
@@ -1,4 +1,5 @@
1
1
  import { _since } from '../datetime/time.util.js';
2
+ import { consoleLogger } from '../log/commonLogger.js';
2
3
  import { _anyToError } from './error.util.js';
3
4
  /**
4
5
  * Decorates a function with "try/catch", so it'll never reject/throw.
@@ -9,7 +10,7 @@ import { _anyToError } from './error.util.js';
9
10
  * @experimental
10
11
  */
11
12
  export function _tryCatch(fn, opt = {}) {
12
- const { onError, logError = true, logSuccess = false, logger = console } = opt;
13
+ const { onError, logError = true, logSuccess = false, logger = consoleLogger } = opt;
13
14
  const fname = fn.name || 'anonymous';
14
15
  return async function (...args) {
15
16
  const started = Date.now();
@@ -22,7 +23,7 @@ export function _tryCatch(fn, opt = {}) {
22
23
  }
23
24
  catch (err) {
24
25
  if (logError) {
25
- logger.warn(`tryCatch.${fname} error in ${_since(started)}:`, err);
26
+ logger.warn({ msg: `tryCatch.${fname} error in ${_since(started)}`, err });
26
27
  }
27
28
  if (onError) {
28
29
  try {
@@ -6,13 +6,13 @@ import { _ms, _since } from '../datetime/time.util.js';
6
6
  import { isServerSide } from '../env.js';
7
7
  import { _assertErrorClassOrRethrow, _assertIsError } from '../error/assert.js';
8
8
  import { _anyToError, _anyToErrorObject, _errorDataAppend, _errorLikeToErrorObject, HttpRequestError, UnexpectedPassError, } from '../error/error.util.js';
9
+ import { consoleLogger } from '../log/commonLogger.js';
9
10
  import { _clamp } from '../number/number.util.js';
10
11
  import { _filterFalsyValues, _filterNullishValues, _filterUndefinedValues, _mapKeys, _merge, _omit, _pick, } from '../object/object.util.js';
11
12
  import { pDelaySignal } from '../promise/pDelay.js';
12
13
  import { pTimeout } from '../promise/pTimeout.js';
13
14
  import { _toUrlOrNull } from '../string/index.js';
14
15
  import { _jsonParse, _jsonParseIfPossible } from '../string/json.util.js';
15
- import { _stringify } from '../string/stringify.js';
16
16
  import { HTTP_METHODS } from './http.model.js';
17
17
  /**
18
18
  * Experimental wrapper around Fetch.
@@ -326,7 +326,7 @@ export class Fetcher {
326
326
  .filter(Boolean)
327
327
  .join(' '));
328
328
  if (req.logRequestBody && req.init.body) {
329
- logger.log(req.init.body); // todo: check if we can _inspect it
329
+ logger.log({ body: req.init.body }); // todo: check if we can _inspect it
330
330
  }
331
331
  }
332
332
  try {
@@ -473,7 +473,7 @@ export class Fetcher {
473
473
  .filter(Boolean)
474
474
  .join(' '));
475
475
  if (req.logResponseBody && res.body !== undefined) {
476
- logger.log(res.body);
476
+ logger.log({ body: res.body });
477
477
  }
478
478
  }
479
479
  }
@@ -558,19 +558,20 @@ export class Fetcher {
558
558
  // lastError = retryStatus.retryStopped
559
559
  // We need to log the response "anyway" if logResponse is true
560
560
  if (res.err && (!retryStatus.retryStopped || res.req.logResponse)) {
561
- this.cfg.logger.error([
562
- ' <<',
563
- res.fetchResponse?.status || 0,
564
- res.signature,
565
- count &&
566
- (retryStatus.retryAttempt || !retryStatus.retryStopped) &&
567
- `try#${retryStatus.retryAttempt + 1}/${count + 1}`,
568
- _since(res.req.started),
569
- ]
570
- .filter(Boolean)
571
- .join(' ') + '\n',
572
- // We're stringifying the error here, otherwise Sentry shows it as [object Object]
573
- _stringify(res.err.cause || res.err));
561
+ this.cfg.logger.error({
562
+ msg: [
563
+ ' <<',
564
+ res.fetchResponse?.status || 0,
565
+ res.signature,
566
+ count &&
567
+ (retryStatus.retryAttempt || !retryStatus.retryStopped) &&
568
+ `try#${retryStatus.retryAttempt + 1}/${count + 1}`,
569
+ _since(res.req.started),
570
+ ]
571
+ .filter(Boolean)
572
+ .join(' '),
573
+ err: res.err.cause || res.err,
574
+ });
574
575
  }
575
576
  if (retryStatus.retryStopped)
576
577
  return;
@@ -704,7 +705,7 @@ export class Fetcher {
704
705
  return shortUrl;
705
706
  }
706
707
  normalizeCfg(cfg) {
707
- const { debug = false, logger = console } = cfg;
708
+ const { debug = false, logger = consoleLogger } = cfg;
708
709
  if (cfg.baseUrl?.endsWith('/')) {
709
710
  logger.warn(`Fetcher: baseUrl should not end with slash: ${cfg.baseUrl}`);
710
711
  cfg.baseUrl = cfg.baseUrl.slice(0, cfg.baseUrl.length - 1);
@@ -124,7 +124,7 @@ export interface FetcherCfg {
124
124
  */
125
125
  logWithSearchParams?: boolean;
126
126
  /**
127
- * Defaults to `console`.
127
+ * Defaults to `consoleLogger`.
128
128
  */
129
129
  logger?: CommonLogger;
130
130
  throwHttpErrors?: boolean;
@@ -1,4 +1,3 @@
1
- import type { AnyObject, MutateOptions } from '../types.js';
2
1
  /**
3
2
  * These levels follow console.* naming,
4
3
  * so you can use console[level] safely.
@@ -16,17 +15,32 @@ import type { AnyObject, MutateOptions } from '../types.js';
16
15
  export type CommonLogLevel = 'debug' | 'log' | 'warn' | 'error';
17
16
  export declare const commonLogLevelNumber: Record<CommonLogLevel, number>;
18
17
  /**
19
- * Function that takes any number of arguments and logs them all.
20
- * It is expected that logged arguments are separated by "space", like console.log does.
21
- *
22
- * @experimental
18
+ * Structured log data: a message, an error, and any other fields.
23
19
  */
24
- export type CommonLogFunction = (...args: any[]) => void;
25
- export type CommonLogWithLevelFunction = (level: CommonLogLevel, args: any[]) => void;
20
+ export interface LogData {
21
+ msg?: string;
22
+ err?: unknown;
23
+ [field: string]: unknown;
24
+ }
25
+ /**
26
+ * What a log call accepts: a string is `{ msg }`, an Error is `{ err }`.
27
+ * A non-Error error value (e.g an `unknown` from a catch) is passed as `{ err }`.
28
+ */
29
+ export type LogInput = string | Error | LogData;
30
+ /**
31
+ * What a sink receives: the logger's context, the log data and the level.
32
+ */
33
+ export interface LogEntry extends LogData {
34
+ level: CommonLogLevel;
35
+ }
36
+ export type LogSink = (entry: LogEntry) => void;
37
+ /**
38
+ * A log call is either one `LogInput`, or a message with the other fields:
39
+ * `logger.log('hey')`, `logger.error(err)`, `logger.warn({ msg, err })`, `logger.warn('failed', { err })`.
40
+ */
41
+ export type CommonLogArgs = [input: LogInput] | [msg: string, data: LogData];
42
+ export type CommonLogFunction = (...args: CommonLogArgs) => void;
26
43
  /**
27
- * Interface is inspired/compatible with `console.*`
28
- * So, `console` is a valid CommonLogger implementation as-is.
29
- *
30
44
  * @experimental
31
45
  */
32
46
  export interface CommonLogger {
@@ -34,38 +48,30 @@ export interface CommonLogger {
34
48
  log: CommonLogFunction;
35
49
  warn: CommonLogFunction;
36
50
  error: CommonLogFunction;
51
+ /**
52
+ * Creates a logger that adds `context` to every entry.
53
+ */
54
+ child: (context: LogData) => CommonLogger;
37
55
  }
38
56
  /**
39
- * SimpleLogger that does nothing (noop).
57
+ * Creates a CommonLogger that writes every entry to `sink`.
58
+ * `context` is called on every log call, so it can return values that change
59
+ * after the logger is created (e.g a request context whose user is resolved later).
40
60
  */
41
- export declare const commonLoggerNoop: CommonLogger;
61
+ export declare function commonLoggerCreate(sink: LogSink, context?: () => LogData): CommonLogger;
42
62
  /**
43
- * Creates a "child" logger that is "limited" to the specified CommonLogLevel.
63
+ * CommonLogger that logs to `console`.
44
64
  */
45
- export declare function createCommonLoggerAtLevel(logger?: CommonLogger, minLevel?: CommonLogLevel, opt?: MutateOptions): CommonLogger;
46
- /**
47
- * Creates a "proxy" CommonLogger that pipes log messages to all provided sub-loggers.
48
- */
49
- export declare function commonLoggerPipe(loggers: CommonLogger[]): CommonLogger;
65
+ export declare const consoleLogger: CommonLogger;
50
66
  /**
51
- * Creates a "child" CommonLogger with prefix (one or multiple).
67
+ * CommonLogger that does nothing (noop).
52
68
  */
53
- export declare function commonLoggerPrefix(logger: CommonLogger, ...prefixes: any[]): CommonLogger;
54
- export type CommonLogSink = CommonLogger | CommonLogWithLevelFunction;
69
+ export declare const commonLoggerNoop: CommonLogger;
55
70
  /**
56
- * A plain object is copied once, at creation.
57
- * A function is called on every log call, for context that changes after the logger is created
58
- * (e.g a request context whose user is resolved later).
71
+ * Creates a CommonLogger that is "limited" to the specified CommonLogLevel.
59
72
  */
60
- export type CommonLogContext = AnyObject | (() => AnyObject);
61
- export interface CommonLoggerWithContext extends CommonLogger {
62
- child: (context: CommonLogContext) => CommonLoggerWithContext;
63
- }
73
+ export declare function createCommonLoggerAtLevel(logger?: CommonLogger, minLevel?: CommonLogLevel): CommonLogger;
64
74
  /**
65
- * Creates a CommonLogger from a single function that takes `level` and `args`.
66
- *
67
- * With a context, every call emits one object: plain-object args merged in, first Error as `err`,
68
- * the rest joined into `msg`.
75
+ * Creates a "proxy" CommonLogger that pipes log entries to all provided sub-loggers.
69
76
  */
70
- export declare function commonLoggerCreate(sink: CommonLogSink): CommonLogger;
71
- export declare function commonLoggerCreate(sink: CommonLogSink, context: CommonLogContext): CommonLoggerWithContext;
77
+ export declare function commonLoggerPipe(loggers: CommonLogger[]): CommonLogger;
@@ -1,58 +1,75 @@
1
- import { _isPlainObject } from '../is.util.js';
2
1
  // copy-pasted to avoid weird circular dependency
3
2
  const _noop = (..._args) => undefined;
4
3
  export const commonLogLevelNumber = {
5
4
  debug: 10,
6
5
  log: 20,
7
- warn: 20,
8
- error: 30,
6
+ warn: 30,
7
+ error: 40,
9
8
  };
10
9
  /**
11
- * SimpleLogger that does nothing (noop).
10
+ * Creates a CommonLogger that writes every entry to `sink`.
11
+ * `context` is called on every log call, so it can return values that change
12
+ * after the logger is created (e.g a request context whose user is resolved later).
13
+ */
14
+ export function commonLoggerCreate(sink, context) {
15
+ const emit = (level, [input, data]) => sink({ ...context?.(), ...toLogData(input, data), level });
16
+ return {
17
+ debug: (...args) => emit('debug', args),
18
+ log: (...args) => emit('log', args),
19
+ warn: (...args) => emit('warn', args),
20
+ error: (...args) => emit('error', args),
21
+ child: childContext => commonLoggerCreate(sink, () => ({ ...context?.(), ...childContext })),
22
+ };
23
+ }
24
+ function toLogData(input, data) {
25
+ if (typeof input === 'string')
26
+ return { ...data, msg: input };
27
+ if (input instanceof Error)
28
+ return { err: input };
29
+ return input;
30
+ }
31
+ /**
32
+ * CommonLogger that logs to `console`.
33
+ */
34
+ export const consoleLogger = commonLoggerCreate(({ level, msg, err, ...fields }) => {
35
+ const args = [];
36
+ if (msg !== undefined)
37
+ args.push(msg);
38
+ if (err !== undefined)
39
+ args.push(err);
40
+ if (Object.keys(fields).length)
41
+ args.push(fields);
42
+ console[level](...args);
43
+ });
44
+ /**
45
+ * CommonLogger that does nothing (noop).
12
46
  */
13
47
  export const commonLoggerNoop = {
14
48
  debug: _noop,
15
49
  log: _noop,
16
50
  warn: _noop,
17
51
  error: _noop,
52
+ child: () => commonLoggerNoop,
18
53
  };
19
54
  /**
20
- * Creates a "child" logger that is "limited" to the specified CommonLogLevel.
55
+ * Creates a CommonLogger that is "limited" to the specified CommonLogLevel.
21
56
  */
22
- export function createCommonLoggerAtLevel(logger = console, minLevel = 'log', opt = {}) {
57
+ export function createCommonLoggerAtLevel(logger = consoleLogger, minLevel = 'log') {
23
58
  const level = commonLogLevelNumber[minLevel];
24
- if (opt.mutate) {
25
- if (level > commonLogLevelNumber['debug']) {
26
- logger.debug = _noop;
27
- if (level > commonLogLevelNumber['log']) {
28
- logger.log = _noop;
29
- if (level > commonLogLevelNumber['warn']) {
30
- logger.warn = _noop;
31
- if (level > commonLogLevelNumber['error']) {
32
- logger.error = _noop;
33
- }
34
- }
35
- }
36
- }
37
- return logger;
38
- }
39
59
  if (level <= commonLogLevelNumber['debug']) {
40
60
  // All levels are kept
41
61
  return logger;
42
62
  }
43
- if (level > commonLogLevelNumber['error']) {
44
- // "Log nothing" logger
45
- return commonLoggerNoop;
46
- }
47
63
  return {
48
64
  debug: _noop, // otherwise it is "log everything" logger (same logger as input)
49
- log: level <= commonLogLevelNumber['log'] ? logger.log.bind(logger) : _noop,
50
- warn: level <= commonLogLevelNumber['warn'] ? logger.warn.bind(logger) : _noop,
51
- error: logger.error.bind(logger), // otherwise it's "log nothing" logger (same as noopLogger)
65
+ log: level <= commonLogLevelNumber['log'] ? (...args) => logger.log(...args) : _noop,
66
+ warn: level <= commonLogLevelNumber['warn'] ? (...args) => logger.warn(...args) : _noop,
67
+ error: (...args) => logger.error(...args),
68
+ child: context => createCommonLoggerAtLevel(logger.child(context), minLevel),
52
69
  };
53
70
  }
54
71
  /**
55
- * Creates a "proxy" CommonLogger that pipes log messages to all provided sub-loggers.
72
+ * Creates a "proxy" CommonLogger that pipes log entries to all provided sub-loggers.
56
73
  */
57
74
  export function commonLoggerPipe(loggers) {
58
75
  return {
@@ -60,68 +77,6 @@ export function commonLoggerPipe(loggers) {
60
77
  log: (...args) => loggers.forEach(logger => logger.log(...args)),
61
78
  warn: (...args) => loggers.forEach(logger => logger.warn(...args)),
62
79
  error: (...args) => loggers.forEach(logger => logger.error(...args)),
80
+ child: context => commonLoggerPipe(loggers.map(logger => logger.child(context))),
63
81
  };
64
82
  }
65
- /**
66
- * Creates a "child" CommonLogger with prefix (one or multiple).
67
- */
68
- export function commonLoggerPrefix(logger, ...prefixes) {
69
- return {
70
- debug: (...args) => logger.debug(...prefixes, ...args),
71
- log: (...args) => logger.log(...prefixes, ...args),
72
- warn: (...args) => logger.warn(...prefixes, ...args),
73
- error: (...args) => logger.error(...prefixes, ...args),
74
- };
75
- }
76
- export function commonLoggerCreate(sink, context) {
77
- const fn = typeof sink === 'function' ? sink : (level, args) => sink[level](...args);
78
- if (!context) {
79
- return {
80
- debug: (...args) => fn('debug', args),
81
- log: (...args) => fn('log', args),
82
- warn: (...args) => fn('warn', args),
83
- error: (...args) => fn('error', args),
84
- };
85
- }
86
- const getContext = contextGetter(context);
87
- const logger = {
88
- debug: (...args) => fn('debug', [toLogEntry(getContext(), args)]),
89
- log: (...args) => fn('log', [toLogEntry(getContext(), args)]),
90
- warn: (...args) => fn('warn', [toLogEntry(getContext(), args)]),
91
- error: (...args) => fn('error', [toLogEntry(getContext(), args)]),
92
- child: c => {
93
- const getChild = contextGetter(c);
94
- return commonLoggerCreate(fn, () => ({ ...getContext(), ...getChild() }));
95
- },
96
- };
97
- return logger;
98
- }
99
- function contextGetter(context) {
100
- // AnyObject also admits functions, so typeof does not narrow the union
101
- if (typeof context === 'function')
102
- return context;
103
- const copy = { ...context };
104
- return () => copy;
105
- }
106
- function toLogEntry(context, args) {
107
- const entry = { ...context };
108
- let err;
109
- let msg;
110
- for (const arg of args) {
111
- if (_isPlainObject(arg)) {
112
- Object.assign(entry, arg);
113
- }
114
- else if (!err && arg instanceof Error) {
115
- err = arg;
116
- }
117
- else {
118
- const s = String(arg);
119
- msg = msg === undefined ? s : `${msg} ${s}`;
120
- }
121
- }
122
- if (err)
123
- entry['err'] = err;
124
- if (msg !== undefined)
125
- entry['msg'] = msg;
126
- return entry;
127
- }
@@ -19,7 +19,7 @@ export interface PGradualQueueCfg {
19
19
  */
20
20
  errorMode?: ErrorMode;
21
21
  /**
22
- * Default to `console`
22
+ * Default to `consoleLogger`
23
23
  */
24
24
  logger?: CommonLogger;
25
25
  /**
@@ -14,11 +14,11 @@ export interface PMapOptions {
14
14
  * When set to THROW_AGGREGATED, instead of stopping when a promise rejects, it will wait for all the promises to settle and then reject with an Aggregated error.
15
15
  *
16
16
  * When set to SUPPRESS - will ignore errors and return results from successful operations.
17
- * When in SUPPRESS - errors are still logged via the `logger` (which defaults to console).
17
+ * When in SUPPRESS - errors are still logged via the `logger` (which defaults to consoleLogger).
18
18
  */
19
19
  errorMode?: ErrorMode;
20
20
  /**
21
- * Default to `console`.
21
+ * Default to `consoleLogger`.
22
22
  * Pass `null` to skip logging completely.
23
23
  */
24
24
  logger?: CommonLogger | null;