@naturalcycles/js-lib 15.89.0 → 15.91.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 +35 -28
  16. package/dist/log/commonLogger.js +51 -86
  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 +2 -2
  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 +78 -118
  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
@@ -67,7 +67,7 @@ export interface PRetryOptions {
67
67
  */
68
68
  logNone?: boolean;
69
69
  /**
70
- * Default to `console`
70
+ * Default to `consoleLogger`
71
71
  */
72
72
  logger?: CommonLogger;
73
73
  /**
@@ -1,5 +1,6 @@
1
1
  import { _since } from '../datetime/time.util.js';
2
2
  import { _errorDataAppend } from '../error/error.util.js';
3
+ import { consoleLogger } from '../log/commonLogger.js';
3
4
  import { pDelay } from './pDelay.js';
4
5
  import { pTimeout } from './pTimeout.js';
5
6
  /**
@@ -12,7 +13,7 @@ export function pRetryFn(fn, opt = {}) {
12
13
  };
13
14
  }
14
15
  export async function pRetry(fn, opt = {}) {
15
- const { maxAttempts = 4, delay: initialDelay = 1000, delayMultiplier = 2, predicate, logger = console, name, timeout, } = opt;
16
+ const { maxAttempts = 4, delay: initialDelay = 1000, delayMultiplier = 2, predicate, logger = consoleLogger, name, timeout, } = opt;
16
17
  const fakeError = timeout ? new Error('TimeoutError') : undefined;
17
18
  let { logFirstAttempt = false, logRetries = true, logFailures = true, logSuccess = false } = opt;
18
19
  if (opt.logAll) {
@@ -52,7 +53,7 @@ export async function pRetry(fn, opt = {}) {
52
53
  if (logFailures) {
53
54
  // Logger at warn (not error) level, because it's not a fatal error, but a retriable one
54
55
  // Fatal one is not logged either, because it's been thrown instead
55
- logger.warn(`${fname} attempt #${attempt} error in ${_since(started)}:`, err);
56
+ logger.warn({ msg: `${fname} attempt #${attempt} error in ${_since(started)}`, err });
56
57
  }
57
58
  if (attempt >= maxAttempts || (predicate && !predicate(err, attempt, maxAttempts))) {
58
59
  // Give up
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/js-lib",
3
3
  "type": "module",
4
- "version": "15.89.0",
4
+ "version": "15.91.0",
5
5
  "dependencies": {
6
6
  "tslib": "^2"
7
7
  },
@@ -14,7 +14,7 @@
14
14
  }
15
15
  },
16
16
  "devDependencies": {
17
- "@naturalcycles/dev-lib": "0.0.0",
17
+ "@naturalcycles/dev-lib": "20.64.0",
18
18
  "@types/node": "^26",
19
19
  "@types/semver": "^7",
20
20
  "typescript": "^7",
@@ -1,8 +1,9 @@
1
+ import { consoleLogger } from './log/commonLogger.js'
1
2
  import type { CommonLogger } from './log/commonLogger.js'
2
3
  import type { NumberOfMilliseconds } from './types.js'
3
4
 
4
5
  class AsyncManagerImpl {
5
- logger: CommonLogger = console
6
+ logger: CommonLogger = consoleLogger
6
7
 
7
8
  pendingOps = new Set<Promise<unknown>>()
8
9
 
@@ -58,7 +59,7 @@ class AsyncManagerImpl {
58
59
  if (this.onErrorHooks.length) {
59
60
  this.onErrorHooks.forEach(hook => hook(err))
60
61
  } else {
61
- this.logger.error('AsyncManager unhandled rejection:', err)
62
+ this.logger.error({ msg: 'AsyncManager unhandled rejection', err })
62
63
  }
63
64
  }
64
65
  }
@@ -1,4 +1,5 @@
1
1
  import { _assert, _assertTypeOf } from '../error/assert.js'
2
+ import { consoleLogger } from '../log/commonLogger.js'
2
3
  import type { CommonLogger } from '../log/commonLogger.js'
3
4
  import type { AnyAsyncFunction, AnyFunction, AnyObject, MaybeParameters } from '../types.js'
4
5
  import { _objectAssign, MISS } from '../types.js'
@@ -20,7 +21,7 @@ export interface AsyncMemoOptions<FN> {
20
21
  cacheKeyFn?: (args: MaybeParameters<FN>) => any
21
22
 
22
23
  /**
23
- * Default to `console`
24
+ * Default to `consoleLogger`
24
25
  */
25
26
  logger?: CommonLogger
26
27
  }
@@ -64,7 +65,7 @@ export const _AsyncMemo =
64
65
  // After it's resolved - it's evicted from the cache and moved to the "proper" `instanceCache`
65
66
  const instancePromiseCache = new Map<AnyObject, Map<any, Promise<any>>>()
66
67
 
67
- const { logger = console, cacheFactory, cacheKeyFn = jsonMemoSerializer } = opt
68
+ const { logger = consoleLogger, cacheFactory, cacheKeyFn = jsonMemoSerializer } = opt
68
69
 
69
70
  const keyStr = String(key)
70
71
  const methodSignature = _getTargetMethodSignature(target, keyStr)
@@ -109,7 +110,7 @@ export const _AsyncMemo =
109
110
  },
110
111
  async err => {
111
112
  // Log the cache error and proceed "as cache Miss"
112
- logger.error(err)
113
+ logger.error({ err })
113
114
  return await onMiss()
114
115
  },
115
116
  )
@@ -130,7 +131,7 @@ export const _AsyncMemo =
130
131
  try {
131
132
  await cache!.set(cacheKey, value)
132
133
  } catch (err) {
133
- logger.error(err) // log and ignore the error
134
+ logger.error({ err }) // log and ignore the error
134
135
  } finally {
135
136
  // Clear the "in-flight" promise cache entry, as we now have a "permanent" cache entry
136
137
  promiseCache!.delete(cacheKey)
@@ -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 type { CommonLogger } from '../log/commonLogger.js'
4
5
  import { SimpleMovingAverage } from '../math/sma.js'
5
6
  import { _stringify } from '../string/stringify.js'
@@ -53,7 +54,7 @@ export interface LogMethodOptions {
53
54
  logResultFn?: LogResultFn
54
55
 
55
56
  /**
56
- * Defaults to `console`
57
+ * Defaults to `consoleLogger`
57
58
  */
58
59
  logger?: CommonLogger
59
60
  }
@@ -84,7 +85,7 @@ export function _LogMethod(opt: LogMethodOptions = {}): MethodDecorator {
84
85
  logStart,
85
86
  logResult,
86
87
  logResultLength = true,
87
- logger = console,
88
+ logger = consoleLogger,
88
89
  } = opt
89
90
  let { logResultFn } = opt
90
91
  if (!logResultFn) {
@@ -156,10 +157,14 @@ function logFinished(
156
157
  }
157
158
 
158
159
  if (err !== undefined) {
159
- t.push('ERROR:', err)
160
+ t.push('ERROR:')
160
161
  } else if (logResultFn) {
161
162
  t.push(...logResultFn(res))
162
163
  }
163
164
 
164
- logger.log(...t.filter(Boolean))
165
+ const msg = t
166
+ .filter(Boolean)
167
+ .map(part => _stringify(part))
168
+ .join(' ')
169
+ logger.log(err === undefined ? msg : { msg, err })
165
170
  }
@@ -1,4 +1,5 @@
1
1
  import { _assert, _assertTypeOf } from '../error/assert.js'
2
+ import { consoleLogger } from '../log/commonLogger.js'
2
3
  import type { CommonLogger } from '../log/commonLogger.js'
3
4
  import type { AnyFunction, AnyObject, MaybeParameters } from '../types.js'
4
5
  import { _objectAssign } from '../types.js'
@@ -21,7 +22,7 @@ export interface MemoOptions<FN> {
21
22
  cacheKeyFn?: (args: MaybeParameters<FN>) => any
22
23
 
23
24
  /**
24
- * Default to `console`
25
+ * Default to `consoleLogger`
25
26
  */
26
27
  logger?: CommonLogger
27
28
  }
@@ -78,7 +79,7 @@ export const _Memo =
78
79
  const instanceCache = new Map<AnyObject, MemoCache>()
79
80
 
80
81
  const {
81
- logger = console,
82
+ logger = consoleLogger,
82
83
  cacheFactory = () => new MapMemoCache(),
83
84
  cacheKeyFn = jsonMemoSerializer,
84
85
  } = opt
@@ -107,7 +108,7 @@ export const _Memo =
107
108
  try {
108
109
  cache.set(cacheKey, value)
109
110
  } catch (err) {
110
- logger.error(err)
111
+ logger.error({ err })
111
112
  }
112
113
 
113
114
  return value
@@ -11,6 +11,7 @@ Otherwise resorts to JSON.stringify.
11
11
  Benchmark shows similar perf for ObjectCache and MapCache.
12
12
  */
13
13
 
14
+ import { consoleLogger } from '../log/commonLogger.js'
14
15
  import type { CommonLogger } from '../log/commonLogger.js'
15
16
  import { _getTargetMethodSignature } from './decorator.util.js'
16
17
  import type { MemoCache } from './memo.util.js'
@@ -55,7 +56,7 @@ export const memoSimple =
55
56
  */
56
57
  const cache: MemoCache = new MapMemoCache()
57
58
 
58
- const { logger = console } = opt
59
+ const { logger = consoleLogger } = opt
59
60
  const keyStr = String(key)
60
61
  const methodSignature = _getTargetMethodSignature(target, keyStr)
61
62
 
@@ -1,3 +1,4 @@
1
+ import { consoleLogger } from '../log/commonLogger.js'
1
2
  import type { AnyFunction, MaybeParameters } from '../types.js'
2
3
  import type { MemoOptions } from './memo.decorator.js'
3
4
  import type { MemoCache } from './memo.util.js'
@@ -17,7 +18,7 @@ export function _memoized<FN extends AnyFunction>(
17
18
  opt: MemoOptions<FN> = {},
18
19
  ): FN & MemoizedFunction {
19
20
  const {
20
- logger = console,
21
+ logger = consoleLogger,
21
22
  cacheFactory = () => new MapMemoCache(),
22
23
  cacheKeyFn = jsonMemoSerializer,
23
24
  } = opt
@@ -37,7 +38,7 @@ export function _memoized<FN extends AnyFunction>(
37
38
  try {
38
39
  cache.set(cacheKey, value)
39
40
  } catch (err) {
40
- logger.error(err)
41
+ logger.error({ err })
41
42
  }
42
43
 
43
44
  return value
@@ -1,3 +1,4 @@
1
+ import { consoleLogger } from '../log/commonLogger.js'
1
2
  import type { AnyAsyncFunction, MaybeParameters } from '../types.js'
2
3
  import { MISS } from '../types.js'
3
4
  import type { AsyncMemoOptions } from './asyncMemo.decorator.js'
@@ -12,7 +13,7 @@ export function _memoizedAsync<FN extends AnyAsyncFunction>(
12
13
  fn: FN,
13
14
  opt: AsyncMemoOptions<FN>,
14
15
  ): FN & MemoizedAsyncFunction {
15
- const { logger = console, cacheFactory, cacheKeyFn = jsonMemoSerializer } = opt
16
+ const { logger = consoleLogger, cacheFactory, cacheKeyFn = jsonMemoSerializer } = opt
16
17
 
17
18
  const cache = cacheFactory()
18
19
 
@@ -24,7 +25,7 @@ export function _memoizedAsync<FN extends AnyAsyncFunction>(
24
25
  try {
25
26
  value = await cache.get(cacheKey)
26
27
  } catch (err) {
27
- logger.error(err)
28
+ logger.error({ err })
28
29
  }
29
30
 
30
31
  if (value !== MISS) {
@@ -37,7 +38,7 @@ export function _memoizedAsync<FN extends AnyAsyncFunction>(
37
38
  try {
38
39
  await cache.set(cacheKey, value)
39
40
  } catch (err) {
40
- logger.error(err)
41
+ logger.error({ err })
41
42
  }
42
43
  })()
43
44
 
@@ -1,4 +1,5 @@
1
1
  import { _since } from '../datetime/time.util.js'
2
+ import { consoleLogger } from '../log/commonLogger.js'
2
3
  import type { CommonLogger } from '../log/commonLogger.js'
3
4
  import type { AnyFunction, UnixTimestampMillis } from '../types.js'
4
5
  import { _anyToError } from './error.util.js'
@@ -21,7 +22,7 @@ export interface TryCatchOptions {
21
22
  logError?: boolean
22
23
 
23
24
  /**
24
- * Default to `console`
25
+ * Default to `consoleLogger`
25
26
  */
26
27
  logger?: CommonLogger
27
28
  }
@@ -35,7 +36,7 @@ export interface TryCatchOptions {
35
36
  * @experimental
36
37
  */
37
38
  export function _tryCatch<T extends AnyFunction>(fn: T, opt: TryCatchOptions = {}): T {
38
- const { onError, logError = true, logSuccess = false, logger = console } = opt
39
+ const { onError, logError = true, logSuccess = false, logger = consoleLogger } = opt
39
40
 
40
41
  const fname = fn.name || 'anonymous'
41
42
 
@@ -52,7 +53,7 @@ export function _tryCatch<T extends AnyFunction>(fn: T, opt: TryCatchOptions = {
52
53
  return r
53
54
  } catch (err) {
54
55
  if (logError) {
55
- logger.warn(`tryCatch.${fname} error in ${_since(started)}:`, err)
56
+ logger.warn({ msg: `tryCatch.${fname} error in ${_since(started)}`, err })
56
57
  }
57
58
 
58
59
  if (onError) {
@@ -162,7 +162,7 @@ export interface FetcherCfg {
162
162
  logWithSearchParams?: boolean
163
163
 
164
164
  /**
165
- * Defaults to `console`.
165
+ * Defaults to `consoleLogger`.
166
166
  */
167
167
  logger?: CommonLogger
168
168
 
@@ -16,6 +16,7 @@ import {
16
16
  HttpRequestError,
17
17
  UnexpectedPassError,
18
18
  } from '../error/error.util.js'
19
+ import { consoleLogger } from '../log/commonLogger.js'
19
20
  import { _clamp } from '../number/number.util.js'
20
21
  import {
21
22
  _filterFalsyValues,
@@ -30,7 +31,6 @@ import { pDelaySignal } from '../promise/pDelay.js'
30
31
  import { pTimeout } from '../promise/pTimeout.js'
31
32
  import { _toUrlOrNull } from '../string/index.js'
32
33
  import { _jsonParse, _jsonParseIfPossible } from '../string/json.util.js'
33
- import { _stringify } from '../string/stringify.js'
34
34
  import type {
35
35
  AnyObject,
36
36
  ErrorDataTuple,
@@ -416,7 +416,7 @@ export class Fetcher {
416
416
  .join(' '),
417
417
  )
418
418
  if (req.logRequestBody && req.init.body) {
419
- logger.log(req.init.body) // todo: check if we can _inspect it
419
+ logger.log({ body: req.init.body }) // todo: check if we can _inspect it
420
420
  }
421
421
  }
422
422
 
@@ -584,7 +584,7 @@ export class Fetcher {
584
584
  )
585
585
 
586
586
  if (req.logResponseBody && res.body !== undefined) {
587
- logger.log(res.body)
587
+ logger.log({ body: res.body })
588
588
  }
589
589
  }
590
590
  }
@@ -688,8 +688,8 @@ export class Fetcher {
688
688
  // lastError = retryStatus.retryStopped
689
689
  // We need to log the response "anyway" if logResponse is true
690
690
  if (res.err && (!retryStatus.retryStopped || res.req.logResponse)) {
691
- this.cfg.logger.error(
692
- [
691
+ this.cfg.logger.error({
692
+ msg: [
693
693
  ' <<',
694
694
  res.fetchResponse?.status || 0,
695
695
  res.signature,
@@ -699,10 +699,9 @@ export class Fetcher {
699
699
  _since(res.req.started),
700
700
  ]
701
701
  .filter(Boolean)
702
- .join(' ') + '\n',
703
- // We're stringifying the error here, otherwise Sentry shows it as [object Object]
704
- _stringify(res.err.cause || res.err),
705
- )
702
+ .join(' '),
703
+ err: res.err.cause || res.err,
704
+ })
706
705
  }
707
706
 
708
707
  if (retryStatus.retryStopped) return
@@ -847,7 +846,7 @@ export class Fetcher {
847
846
  }
848
847
 
849
848
  private normalizeCfg(cfg: FetcherCfg & FetcherOptions): FetcherNormalizedCfg {
850
- const { debug = false, logger = console } = cfg
849
+ const { debug = false, logger = consoleLogger } = cfg
851
850
 
852
851
  if (cfg.baseUrl?.endsWith('/')) {
853
852
  logger.warn(`Fetcher: baseUrl should not end with slash: ${cfg.baseUrl}`)
@@ -1,6 +1,3 @@
1
- import { _isPlainObject } from '../is.util.js'
2
- import type { AnyObject, MutateOptions } from '../types.js'
3
-
4
1
  // copy-pasted to avoid weird circular dependency
5
2
  const _noop = (..._args: any[]): undefined => undefined
6
3
 
@@ -23,23 +20,37 @@ export type CommonLogLevel = 'debug' | 'log' | 'warn' | 'error'
23
20
  export const commonLogLevelNumber: Record<CommonLogLevel, number> = {
24
21
  debug: 10,
25
22
  log: 20,
26
- warn: 20,
27
- error: 30,
23
+ warn: 30,
24
+ error: 40,
28
25
  }
29
26
 
30
27
  /**
31
- * Function that takes any number of arguments and logs them all.
32
- * It is expected that logged arguments are separated by "space", like console.log does.
33
- *
34
- * @experimental
28
+ * Structured log data: a message, an error, and any other fields.
35
29
  */
36
- export type CommonLogFunction = (...args: any[]) => void
37
- export type CommonLogWithLevelFunction = (level: CommonLogLevel, args: any[]) => void
30
+ export interface LogData {
31
+ msg?: string
32
+ err?: unknown
33
+ [field: string]: unknown
34
+ }
35
+
36
+ /**
37
+ * What a log call accepts: a string is `{ msg }`, an Error is `{ err }`.
38
+ * A non-Error error value (e.g an `unknown` from a catch) is passed as `{ err }`.
39
+ */
40
+ export type LogInput = string | Error | LogData
41
+
42
+ /**
43
+ * What a sink receives: the logger's context, the log data and the level.
44
+ */
45
+ export interface LogEntry extends LogData {
46
+ level: CommonLogLevel
47
+ }
48
+
49
+ export type LogSink = (entry: LogEntry) => void
50
+
51
+ export type CommonLogFunction = (input: LogInput) => void
38
52
 
39
53
  /**
40
- * Interface is inspired/compatible with `console.*`
41
- * So, `console` is a valid CommonLogger implementation as-is.
42
- *
43
54
  * @experimental
44
55
  */
45
56
  export interface CommonLogger {
@@ -47,141 +58,90 @@ export interface CommonLogger {
47
58
  log: CommonLogFunction
48
59
  warn: CommonLogFunction
49
60
  error: CommonLogFunction
61
+ /**
62
+ * Creates a logger that adds `context` to every entry.
63
+ */
64
+ child: (context: LogData) => CommonLogger
50
65
  }
51
66
 
52
67
  /**
53
- * SimpleLogger that does nothing (noop).
68
+ * Creates a CommonLogger that writes every entry to `sink`.
69
+ * `context` is called on every log call, so it can return values that change
70
+ * after the logger is created (e.g a request context whose user is resolved later).
71
+ */
72
+ export function commonLoggerCreate(sink: LogSink, context?: () => LogData): CommonLogger {
73
+ const emit = (level: CommonLogLevel, input: LogInput): void =>
74
+ sink({ ...context?.(), ...toLogData(input), level })
75
+
76
+ return {
77
+ debug: input => emit('debug', input),
78
+ log: input => emit('log', input),
79
+ warn: input => emit('warn', input),
80
+ error: input => emit('error', input),
81
+ child: childContext => commonLoggerCreate(sink, () => ({ ...context?.(), ...childContext })),
82
+ }
83
+ }
84
+
85
+ function toLogData(input: LogInput): LogData {
86
+ if (typeof input === 'string') return { msg: input }
87
+ if (input instanceof Error) return { err: input }
88
+ return input
89
+ }
90
+
91
+ /**
92
+ * CommonLogger that logs to `console`.
93
+ */
94
+ export const consoleLogger: CommonLogger = commonLoggerCreate(({ level, msg, err, ...fields }) => {
95
+ const args: unknown[] = []
96
+ if (msg !== undefined) args.push(msg)
97
+ if (err !== undefined) args.push(err)
98
+ if (Object.keys(fields).length) args.push(fields)
99
+ console[level](...args)
100
+ })
101
+
102
+ /**
103
+ * CommonLogger that does nothing (noop).
54
104
  */
55
105
  export const commonLoggerNoop: CommonLogger = {
56
106
  debug: _noop,
57
107
  log: _noop,
58
108
  warn: _noop,
59
109
  error: _noop,
110
+ child: () => commonLoggerNoop,
60
111
  }
61
112
 
62
113
  /**
63
- * Creates a "child" logger that is "limited" to the specified CommonLogLevel.
114
+ * Creates a CommonLogger that is "limited" to the specified CommonLogLevel.
64
115
  */
65
116
  export function createCommonLoggerAtLevel(
66
- logger: CommonLogger = console,
117
+ logger: CommonLogger = consoleLogger,
67
118
  minLevel: CommonLogLevel = 'log',
68
- opt: MutateOptions = {},
69
119
  ): CommonLogger {
70
120
  const level = commonLogLevelNumber[minLevel]
71
- if (opt.mutate) {
72
- if (level > commonLogLevelNumber['debug']) {
73
- logger.debug = _noop
74
- if (level > commonLogLevelNumber['log']) {
75
- logger.log = _noop
76
- if (level > commonLogLevelNumber['warn']) {
77
- logger.warn = _noop
78
- if (level > commonLogLevelNumber['error']) {
79
- logger.error = _noop
80
- }
81
- }
82
- }
83
- }
84
- return logger
85
- }
86
121
 
87
122
  if (level <= commonLogLevelNumber['debug']) {
88
123
  // All levels are kept
89
124
  return logger
90
125
  }
91
126
 
92
- if (level > commonLogLevelNumber['error']) {
93
- // "Log nothing" logger
94
- return commonLoggerNoop
95
- }
96
-
97
127
  return {
98
128
  debug: _noop, // otherwise it is "log everything" logger (same logger as input)
99
- log: level <= commonLogLevelNumber['log'] ? logger.log.bind(logger) : _noop,
100
- warn: level <= commonLogLevelNumber['warn'] ? logger.warn.bind(logger) : _noop,
101
- error: logger.error.bind(logger), // otherwise it's "log nothing" logger (same as noopLogger)
129
+ log: level <= commonLogLevelNumber['log'] ? input => logger.log(input) : _noop,
130
+ warn: level <= commonLogLevelNumber['warn'] ? input => logger.warn(input) : _noop,
131
+ error: input => logger.error(input),
132
+ child: context => createCommonLoggerAtLevel(logger.child(context), minLevel),
102
133
  }
103
134
  }
104
135
 
105
136
  /**
106
- * Creates a "proxy" CommonLogger that pipes log messages to all provided sub-loggers.
137
+ * Creates a "proxy" CommonLogger that pipes log entries to all provided sub-loggers.
107
138
  */
108
139
  export function commonLoggerPipe(loggers: CommonLogger[]): CommonLogger {
109
140
  return {
110
- debug: (...args) => loggers.forEach(logger => logger.debug(...args)),
111
- log: (...args) => loggers.forEach(logger => logger.log(...args)),
112
- warn: (...args) => loggers.forEach(logger => logger.warn(...args)),
113
- error: (...args) => loggers.forEach(logger => logger.error(...args)),
114
- }
115
- }
116
-
117
- /**
118
- * Creates a "child" CommonLogger with prefix (one or multiple).
119
- */
120
- export function commonLoggerPrefix(logger: CommonLogger, ...prefixes: any[]): CommonLogger {
121
- return {
122
- debug: (...args) => logger.debug(...prefixes, ...args),
123
- log: (...args) => logger.log(...prefixes, ...args),
124
- warn: (...args) => logger.warn(...prefixes, ...args),
125
- error: (...args) => logger.error(...prefixes, ...args),
126
- }
127
- }
128
-
129
- export type CommonLogSink = CommonLogger | CommonLogWithLevelFunction
130
-
131
- export interface CommonLoggerWithContext extends CommonLogger {
132
- child: (context: AnyObject) => CommonLoggerWithContext
133
- }
134
-
135
- /**
136
- * Creates a CommonLogger from a single function that takes `level` and `args`.
137
- *
138
- * With a context, every call emits one object: plain-object args merged in, first Error as `err`,
139
- * the rest joined into `msg`.
140
- */
141
- export function commonLoggerCreate(sink: CommonLogSink): CommonLogger
142
- export function commonLoggerCreate(sink: CommonLogSink, context: AnyObject): CommonLoggerWithContext
143
- export function commonLoggerCreate(sink: CommonLogSink, context?: AnyObject): CommonLogger {
144
- const fn: CommonLogWithLevelFunction =
145
- typeof sink === 'function' ? sink : (level, args) => sink[level](...args)
146
-
147
- if (!context) {
148
- return {
149
- debug: (...args) => fn('debug', args),
150
- log: (...args) => fn('log', args),
151
- warn: (...args) => fn('warn', args),
152
- error: (...args) => fn('error', args),
153
- }
141
+ debug: input => loggers.forEach(logger => logger.debug(input)),
142
+ log: input => loggers.forEach(logger => logger.log(input)),
143
+ warn: input => loggers.forEach(logger => logger.warn(input)),
144
+ error: input => loggers.forEach(logger => logger.error(input)),
145
+ child: context => commonLoggerPipe(loggers.map(logger => logger.child(context))),
154
146
  }
155
-
156
- const ctx: AnyObject = { ...context }
157
-
158
- const logger: CommonLoggerWithContext = {
159
- debug: (...args) => fn('debug', [toLogEntry(ctx, args)]),
160
- log: (...args) => fn('log', [toLogEntry(ctx, args)]),
161
- warn: (...args) => fn('warn', [toLogEntry(ctx, args)]),
162
- error: (...args) => fn('error', [toLogEntry(ctx, args)]),
163
- child: c => commonLoggerCreate(fn, { ...ctx, ...c }),
164
- }
165
- return logger
166
- }
167
-
168
- function toLogEntry(context: AnyObject, args: any[]): AnyObject {
169
- const entry: AnyObject = { ...context }
170
- let err: Error | undefined
171
- let msg: string | undefined
172
-
173
- for (const arg of args) {
174
- if (_isPlainObject(arg)) {
175
- Object.assign(entry, arg)
176
- } else if (!err && arg instanceof Error) {
177
- err = arg
178
- } else {
179
- const s = String(arg)
180
- msg = msg === undefined ? s : `${msg} ${s}`
181
- }
182
- }
183
-
184
- if (err) entry['err'] = err
185
- if (msg !== undefined) entry['msg'] = msg
186
- return entry
187
147
  }
@@ -26,7 +26,7 @@ export interface PGradualQueueCfg {
26
26
  errorMode?: ErrorMode
27
27
 
28
28
  /**
29
- * Default to `console`
29
+ * Default to `consoleLogger`
30
30
  */
31
31
  logger?: CommonLogger
32
32