@naturalcycles/backend-lib 9.70.1 → 9.71.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.
@@ -1,4 +1,7 @@
1
1
  import { inspect } from 'node:util';
2
+ import { _isPlainObject } from '@naturalcycles/js-lib';
3
+ import { _anyToErrorObject } from '@naturalcycles/js-lib/error';
4
+ import { _safeJsonStringify } from '@naturalcycles/js-lib/string/safeJsonStringify.js';
2
5
  import { _objectAssign } from '@naturalcycles/js-lib/types';
3
6
  import { _inspect } from '@naturalcycles/nodejs-lib';
4
7
  import { dimGrey } from '@naturalcycles/nodejs-lib/colors';
@@ -41,17 +44,46 @@ export const ciLogger = {
41
44
  // Documented here: https://cloud.google.com/logging/docs/structured-logging
42
45
  // Cloud Run logging: https://cloud.google.com/run/docs/logging
43
46
  function writeGCPStructuredLog(meta, args) {
44
- console.log(JSON.stringify({
45
- message: args.map(a => (typeof a === 'string' ? a : inspect(a))).join(' '),
46
- ...meta,
47
- }));
47
+ if (args.length !== 1 || !_isPlainObject(args[0])) {
48
+ console.log(JSON.stringify({
49
+ message: args.map(a => (typeof a === 'string' ? a : inspect(a))).join(' '),
50
+ ...meta,
51
+ }));
52
+ return;
53
+ }
54
+ const { msg, err, ...entry } = args[0];
55
+ if (err instanceof Error) {
56
+ entry['err'] = _anyToErrorObject(err);
57
+ entry['message'] = msg ? `${msg}\n${inspect(err)}` : inspect(err);
58
+ }
59
+ else {
60
+ if (err !== undefined)
61
+ entry['err'] = err;
62
+ if (msg !== undefined)
63
+ entry['message'] = msg;
64
+ }
65
+ console.log(_safeJsonStringify({ ...entry, ...meta }));
48
66
  }
49
67
  function logToDev(requestId, args) {
50
68
  // Run on local machine
51
- console.log([
52
- requestId ? [dimGrey(`[${requestId}]`)] : [],
53
- ...args.map(a => _inspect(a, { includeErrorStack: true, colors: true })),
54
- ].join(' '));
69
+ if (args.length !== 1 || !_isPlainObject(args[0])) {
70
+ console.log([
71
+ requestId ? [dimGrey(`[${requestId}]`)] : [],
72
+ ...args.map(a => _inspect(a, { includeErrorStack: true, colors: true })),
73
+ ].join(' '));
74
+ return;
75
+ }
76
+ const { msg, err, ...rest } = args[0];
77
+ const parts = [];
78
+ if (requestId)
79
+ parts.push(dimGrey(`[${requestId}]`));
80
+ if (msg !== undefined)
81
+ parts.push(String(msg));
82
+ if (err !== undefined)
83
+ parts.push(_inspect(err, { includeErrorStack: true, colors: true }));
84
+ if (Object.keys(rest).length)
85
+ parts.push(dimGrey(_inspect(rest, { colors: false })));
86
+ console.log(parts.join(' '));
55
87
  }
56
88
  /**
57
89
  * Same as logToDev, but without request and without colors.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/backend-lib",
3
3
  "type": "module",
4
- "version": "9.70.1",
4
+ "version": "9.71.0",
5
5
  "dependencies": {
6
6
  "@naturalcycles/db-lib": "^10",
7
7
  "@naturalcycles/js-lib": "^15",
@@ -1,8 +1,4 @@
1
- import type {
2
- EventLoopMonitorOptions,
3
- EventLoopUtilization,
4
- IntervalHistogram,
5
- } from 'node:perf_hooks'
1
+ import type { ELDHistogram, EventLoopMonitorOptions, EventLoopUtilization } from 'node:perf_hooks'
6
2
  import { monitorEventLoopDelay, performance, PerformanceObserver } from 'node:perf_hooks'
7
3
  import type {
8
4
  Integer,
@@ -45,7 +41,7 @@ export class EventLoopMonitor {
45
41
  constructor(cfg: EventLoopMonitorCfg = {}) {
46
42
  const { measureInterval = 60_000 } = cfg
47
43
 
48
- const eldOptions: EventLoopMonitorOptions2 = {
44
+ const eldOptions: EventLoopMonitorOptions = {
49
45
  // Node >=24.19 / >=26.5: sample once per event loop iteration (uv_prepare/uv_check hooks),
50
46
  // which measures actual per-iteration delay instead of timer jitter.
51
47
  // More accurate (no `resolution` offset baked into every percentile) and cheaper.
@@ -103,7 +99,7 @@ export class EventLoopMonitor {
103
99
  }
104
100
 
105
101
  private interval: NodeJS.Timeout
106
- private eld: IntervalHistogram
102
+ private eld: ELDHistogram
107
103
  private lastElu: EventLoopUtilization
108
104
  /**
109
105
  * Undefined until the first interval has completed.
@@ -167,11 +163,3 @@ export interface EventLoopStats {
167
163
  */
168
164
  gcCPU: Integer
169
165
  }
170
-
171
- /**
172
- * Extends the Node types with `samplePerIteration` (added in Node 24.19 / 26.5).
173
- * Remove once @types/node declares it.
174
- */
175
- interface EventLoopMonitorOptions2 extends EventLoopMonitorOptions {
176
- samplePerIteration?: boolean
177
- }
@@ -1,5 +1,8 @@
1
1
  import { inspect } from 'node:util'
2
+ import { _isPlainObject } from '@naturalcycles/js-lib'
3
+ import { _anyToErrorObject } from '@naturalcycles/js-lib/error'
2
4
  import type { CommonLogger } from '@naturalcycles/js-lib/log'
5
+ import { _safeJsonStringify } from '@naturalcycles/js-lib/string/safeJsonStringify.js'
3
6
  import { _objectAssign } from '@naturalcycles/js-lib/types'
4
7
  import type { AnyObject } from '@naturalcycles/js-lib/types'
5
8
  import { _inspect } from '@naturalcycles/nodejs-lib'
@@ -50,22 +53,46 @@ export const ciLogger: CommonLogger = {
50
53
  // Documented here: https://cloud.google.com/logging/docs/structured-logging
51
54
  // Cloud Run logging: https://cloud.google.com/run/docs/logging
52
55
  function writeGCPStructuredLog(meta: AnyObject, args: any[]): void {
53
- console.log(
54
- JSON.stringify({
55
- message: args.map(a => (typeof a === 'string' ? a : inspect(a))).join(' '),
56
- ...meta,
57
- }),
58
- )
56
+ if (args.length !== 1 || !_isPlainObject(args[0])) {
57
+ console.log(
58
+ JSON.stringify({
59
+ message: args.map(a => (typeof a === 'string' ? a : inspect(a))).join(' '),
60
+ ...meta,
61
+ }),
62
+ )
63
+ return
64
+ }
65
+
66
+ const { msg, err, ...entry } = args[0]
67
+ if (err instanceof Error) {
68
+ entry['err'] = _anyToErrorObject(err)
69
+ entry['message'] = msg ? `${msg}\n${inspect(err)}` : inspect(err)
70
+ } else {
71
+ if (err !== undefined) entry['err'] = err
72
+ if (msg !== undefined) entry['message'] = msg
73
+ }
74
+ console.log(_safeJsonStringify({ ...entry, ...meta }))
59
75
  }
60
76
 
61
77
  function logToDev(requestId: string | null, args: any[]): void {
62
78
  // Run on local machine
63
- console.log(
64
- [
65
- requestId ? [dimGrey(`[${requestId}]`)] : [],
66
- ...args.map(a => _inspect(a, { includeErrorStack: true, colors: true })),
67
- ].join(' '),
68
- )
79
+ if (args.length !== 1 || !_isPlainObject(args[0])) {
80
+ console.log(
81
+ [
82
+ requestId ? [dimGrey(`[${requestId}]`)] : [],
83
+ ...args.map(a => _inspect(a, { includeErrorStack: true, colors: true })),
84
+ ].join(' '),
85
+ )
86
+ return
87
+ }
88
+
89
+ const { msg, err, ...rest } = args[0]
90
+ const parts: string[] = []
91
+ if (requestId) parts.push(dimGrey(`[${requestId}]`))
92
+ if (msg !== undefined) parts.push(String(msg))
93
+ if (err !== undefined) parts.push(_inspect(err, { includeErrorStack: true, colors: true }))
94
+ if (Object.keys(rest).length) parts.push(dimGrey(_inspect(rest, { colors: false })))
95
+ console.log(parts.join(' '))
69
96
  }
70
97
 
71
98
  /**