pino-testkit 10.4.5

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 (196) hide show
  1. package/.nojekyll +0 -0
  2. package/.prettierignore +1 -0
  3. package/CNAME +1 -0
  4. package/CONTRIBUTING.md +30 -0
  5. package/LICENSE +21 -0
  6. package/README.md +175 -0
  7. package/SECURITY.md +87 -0
  8. package/benchmarks/basic.bench.js +95 -0
  9. package/benchmarks/child-child.bench.js +52 -0
  10. package/benchmarks/child-creation.bench.js +73 -0
  11. package/benchmarks/child.bench.js +62 -0
  12. package/benchmarks/deep-object.bench.js +88 -0
  13. package/benchmarks/formatters.bench.js +50 -0
  14. package/benchmarks/internal/custom-levels.js +67 -0
  15. package/benchmarks/internal/just-pino-heavy.bench.js +76 -0
  16. package/benchmarks/internal/just-pino.bench.js +182 -0
  17. package/benchmarks/internal/parent-vs-child.bench.js +75 -0
  18. package/benchmarks/internal/redact.bench.js +86 -0
  19. package/benchmarks/long-string.bench.js +81 -0
  20. package/benchmarks/multi-arg.bench.js +193 -0
  21. package/benchmarks/multistream.js +98 -0
  22. package/benchmarks/object.bench.js +82 -0
  23. package/benchmarks/utils/generate-benchmark-doc.js +36 -0
  24. package/benchmarks/utils/runbench.js +138 -0
  25. package/benchmarks/utils/wrap-log-level.js +55 -0
  26. package/bin.js +6 -0
  27. package/browser.js +547 -0
  28. package/build/sync-version.js +25 -0
  29. package/docs/api.md +1593 -0
  30. package/docs/asynchronous.md +40 -0
  31. package/docs/benchmarks.md +55 -0
  32. package/docs/browser.md +278 -0
  33. package/docs/bundling.md +40 -0
  34. package/docs/child-loggers.md +95 -0
  35. package/docs/diagnostics.md +16 -0
  36. package/docs/ecosystem.md +86 -0
  37. package/docs/help.md +345 -0
  38. package/docs/lts.md +64 -0
  39. package/docs/pretty.md +35 -0
  40. package/docs/redaction.md +135 -0
  41. package/docs/transports.md +1376 -0
  42. package/docs/web.md +309 -0
  43. package/eslint.config.js +28 -0
  44. package/examples/basic.js +43 -0
  45. package/examples/transport.js +68 -0
  46. package/favicon.ico +0 -0
  47. package/file.js +12 -0
  48. package/inc-version.sh +42 -0
  49. package/index.html +55 -0
  50. package/lib/caller.js +30 -0
  51. package/lib/constants.js +28 -0
  52. package/lib/contract.js +8 -0
  53. package/lib/deprecations.js +8 -0
  54. package/lib/levels.js +241 -0
  55. package/lib/meta.js +3 -0
  56. package/lib/multistream.js +203 -0
  57. package/lib/proto.js +256 -0
  58. package/lib/redaction.js +114 -0
  59. package/lib/symbols.js +74 -0
  60. package/lib/time.js +39 -0
  61. package/lib/tools.js +427 -0
  62. package/lib/transport-stream.js +56 -0
  63. package/lib/transport.js +289 -0
  64. package/lib/worker.js +194 -0
  65. package/package.json +119 -0
  66. package/pino.d.ts +908 -0
  67. package/pino.js +240 -0
  68. package/test/basic.test.js +886 -0
  69. package/test/broken-pipe.test.js +59 -0
  70. package/test/browser-child.test.js +132 -0
  71. package/test/browser-disabled.test.js +87 -0
  72. package/test/browser-early-console-freeze.test.js +12 -0
  73. package/test/browser-is-level-enabled.test.js +101 -0
  74. package/test/browser-levels.test.js +241 -0
  75. package/test/browser-serializers.test.js +352 -0
  76. package/test/browser-timestamp.test.js +88 -0
  77. package/test/browser-transmit.test.js +417 -0
  78. package/test/browser.test.js +698 -0
  79. package/test/complex-objects.test.js +36 -0
  80. package/test/crlf.test.js +34 -0
  81. package/test/custom-levels.test.js +267 -0
  82. package/test/diagnostics.test.js +107 -0
  83. package/test/error-key.test.js +37 -0
  84. package/test/error.test.js +403 -0
  85. package/test/escaping.test.js +93 -0
  86. package/test/esm/esm.mjs +14 -0
  87. package/test/esm/index.test.js +21 -0
  88. package/test/esm/named-exports.mjs +29 -0
  89. package/test/exit.test.js +79 -0
  90. package/test/fixtures/broken-pipe/basic.js +9 -0
  91. package/test/fixtures/broken-pipe/destination.js +10 -0
  92. package/test/fixtures/broken-pipe/syncfalse.js +12 -0
  93. package/test/fixtures/console-transport.js +13 -0
  94. package/test/fixtures/crashing-transport.js +13 -0
  95. package/test/fixtures/default-exit.js +8 -0
  96. package/test/fixtures/destination-exit.js +8 -0
  97. package/test/fixtures/eval/index.js +13 -0
  98. package/test/fixtures/eval/node_modules/14-files.js +3 -0
  99. package/test/fixtures/eval/node_modules/2-files.js +3 -0
  100. package/test/fixtures/eval/node_modules/file1.js +5 -0
  101. package/test/fixtures/eval/node_modules/file10.js +5 -0
  102. package/test/fixtures/eval/node_modules/file11.js +5 -0
  103. package/test/fixtures/eval/node_modules/file12.js +5 -0
  104. package/test/fixtures/eval/node_modules/file13.js +5 -0
  105. package/test/fixtures/eval/node_modules/file14.js +11 -0
  106. package/test/fixtures/eval/node_modules/file2.js +5 -0
  107. package/test/fixtures/eval/node_modules/file3.js +5 -0
  108. package/test/fixtures/eval/node_modules/file4.js +5 -0
  109. package/test/fixtures/eval/node_modules/file5.js +5 -0
  110. package/test/fixtures/eval/node_modules/file6.js +5 -0
  111. package/test/fixtures/eval/node_modules/file7.js +5 -0
  112. package/test/fixtures/eval/node_modules/file8.js +5 -0
  113. package/test/fixtures/eval/node_modules/file9.js +5 -0
  114. package/test/fixtures/noop-transport.js +10 -0
  115. package/test/fixtures/pretty/null-prototype.js +8 -0
  116. package/test/fixtures/stdout-hack-protection.js +11 -0
  117. package/test/fixtures/syncfalse-child.js +6 -0
  118. package/test/fixtures/syncfalse-exit.js +9 -0
  119. package/test/fixtures/syncfalse-flush-exit.js +10 -0
  120. package/test/fixtures/syncfalse.js +6 -0
  121. package/test/fixtures/syntax-error-esm.mjs +2 -0
  122. package/test/fixtures/to-file-transport-with-transform.js +20 -0
  123. package/test/fixtures/to-file-transport.js +13 -0
  124. package/test/fixtures/to-file-transport.mjs +8 -0
  125. package/test/fixtures/transport/index.js +12 -0
  126. package/test/fixtures/transport/package.json +5 -0
  127. package/test/fixtures/transport-exit-immediately-with-async-dest.js +16 -0
  128. package/test/fixtures/transport-exit-immediately.js +11 -0
  129. package/test/fixtures/transport-exit-on-ready.js +12 -0
  130. package/test/fixtures/transport-invalid-node-options.js +26 -0
  131. package/test/fixtures/transport-main.js +9 -0
  132. package/test/fixtures/transport-many-lines.js +29 -0
  133. package/test/fixtures/transport-preload-main.mjs +13 -0
  134. package/test/fixtures/transport-preload.mjs +13 -0
  135. package/test/fixtures/transport-string-stdout.js +9 -0
  136. package/test/fixtures/transport-transform.js +21 -0
  137. package/test/fixtures/transport-uses-pino-config.js +33 -0
  138. package/test/fixtures/transport-with-on-exit.js +12 -0
  139. package/test/fixtures/transport-worker-data.js +19 -0
  140. package/test/fixtures/transport-worker-name.js +24 -0
  141. package/test/fixtures/transport-worker.js +15 -0
  142. package/test/fixtures/transport-wrong-export-type.js +3 -0
  143. package/test/fixtures/ts/to-file-transport-native.mts +15 -0
  144. package/test/fixtures/ts/to-file-transport-with-transform.ts +18 -0
  145. package/test/fixtures/ts/to-file-transport.ts +11 -0
  146. package/test/fixtures/ts/transpile.cjs +36 -0
  147. package/test/fixtures/ts/transport-exit-immediately-with-async-dest.ts +15 -0
  148. package/test/fixtures/ts/transport-exit-immediately.ts +10 -0
  149. package/test/fixtures/ts/transport-exit-on-ready.ts +11 -0
  150. package/test/fixtures/ts/transport-main.ts +8 -0
  151. package/test/fixtures/ts/transport-string-stdout.ts +8 -0
  152. package/test/fixtures/ts/transport-worker.ts +14 -0
  153. package/test/formatters.test.js +364 -0
  154. package/test/helper.d.ts +4 -0
  155. package/test/helper.js +155 -0
  156. package/test/hooks.test.js +114 -0
  157. package/test/http.test.js +214 -0
  158. package/test/internals/version.test.js +17 -0
  159. package/test/is-level-enabled.test.js +179 -0
  160. package/test/jest/basic.spec.js +10 -0
  161. package/test/levels.test.js +810 -0
  162. package/test/metadata.test.js +120 -0
  163. package/test/mixin-merge-strategy.test.js +57 -0
  164. package/test/mixin.test.js +241 -0
  165. package/test/multistream.test.js +729 -0
  166. package/test/redact.test.js +893 -0
  167. package/test/serializers.test.js +257 -0
  168. package/test/stdout-protection.test.js +41 -0
  169. package/test/syncfalse.test.js +186 -0
  170. package/test/timestamp-nano.test.js +37 -0
  171. package/test/timestamp.test.js +124 -0
  172. package/test/transport/big.test.js +42 -0
  173. package/test/transport/bundlers-support.test.js +99 -0
  174. package/test/transport/caller.test.js +24 -0
  175. package/test/transport/core.test.js +733 -0
  176. package/test/transport/core.transpiled.test.ts +114 -0
  177. package/test/transport/crash.test.js +36 -0
  178. package/test/transport/module-link.test.js +241 -0
  179. package/test/transport/native-type-stripping.test.mjs +151 -0
  180. package/test/transport/node-options.test.js +116 -0
  181. package/test/transport/pipeline.test.js +137 -0
  182. package/test/transport/preload.test.js +54 -0
  183. package/test/transport/repl.test.js +15 -0
  184. package/test/transport/sync-false.test.js +67 -0
  185. package/test/transport/sync-true.test.js +57 -0
  186. package/test/transport/targets.test.js +48 -0
  187. package/test/transport/uses-pino-config.test.js +166 -0
  188. package/test/transport-stream.test.js +40 -0
  189. package/test/types/pino-import.test-d.cts +30 -0
  190. package/test/types/pino-multistream.test-d.ts +28 -0
  191. package/test/types/pino-top-export.test-d.ts +34 -0
  192. package/test/types/pino-transport.test-d.ts +156 -0
  193. package/test/types/pino-type-only.test-d.ts +75 -0
  194. package/test/types/pino.test-d.ts +683 -0
  195. package/test/types/pino.ts +91 -0
  196. package/tsconfig.json +14 -0
@@ -0,0 +1,114 @@
1
+ 'use strict'
2
+
3
+ const Redact = require('@pinojs/redact')
4
+ const { redactFmtSym, wildcardFirstSym } = require('./symbols')
5
+
6
+ // Custom rx regex equivalent to fast-redact's rx
7
+ const rx = /[^.[\]]+|\[([^[\]]*?)\]/g
8
+
9
+ const CENSOR = '[Redacted]'
10
+ const strict = false // TODO should this be configurable?
11
+
12
+ function redaction (opts, serialize) {
13
+ const { paths, censor, remove } = handle(opts)
14
+
15
+ const shape = paths.reduce((o, str) => {
16
+ rx.lastIndex = 0
17
+ const first = rx.exec(str)
18
+ const next = rx.exec(str)
19
+
20
+ // ns is the top-level path segment, brackets + quoting removed.
21
+ let ns = first[1] !== undefined
22
+ ? first[1].replace(/^(?:"|'|`)(.*)(?:"|'|`)$/, '$1')
23
+ : first[0]
24
+
25
+ if (ns === '*') {
26
+ ns = wildcardFirstSym
27
+ }
28
+
29
+ // top level key:
30
+ if (next === null) {
31
+ o[ns] = null
32
+ return o
33
+ }
34
+
35
+ // path with at least two segments:
36
+ // if ns is already redacted at the top level, ignore lower level redactions
37
+ if (o[ns] === null) {
38
+ return o
39
+ }
40
+
41
+ const { index } = next
42
+ const nextPath = `${str.substr(index, str.length - 1)}`
43
+
44
+ o[ns] = o[ns] || []
45
+
46
+ // shape is a mix of paths beginning with literal values and wildcard
47
+ // paths [ "a.b.c", "*.b.z" ] should reduce to a shape of
48
+ // { "a": [ "b.c", "b.z" ], *: [ "b.z" ] }
49
+ // note: "b.z" is in both "a" and * arrays because "a" matches the wildcard.
50
+ // (* entry has wildcardFirstSym as key)
51
+ if (ns !== wildcardFirstSym && o[ns].length === 0) {
52
+ // first time ns's get all '*' redactions so far
53
+ o[ns].push(...(o[wildcardFirstSym] || []))
54
+ }
55
+
56
+ if (ns === wildcardFirstSym) {
57
+ // new * path gets added to all previously registered literal ns's.
58
+ Object.keys(o).forEach(function (k) {
59
+ if (o[k]) {
60
+ o[k].push(nextPath)
61
+ }
62
+ })
63
+ }
64
+
65
+ o[ns].push(nextPath)
66
+ return o
67
+ }, {})
68
+
69
+ // the redactor assigned to the format symbol key
70
+ // provides top level redaction for instances where
71
+ // an object is interpolated into the msg string
72
+ const result = {
73
+ [redactFmtSym]: Redact({ paths, censor, serialize, strict, remove })
74
+ }
75
+
76
+ const topCensor = (...args) => {
77
+ return typeof censor === 'function' ? serialize(censor(...args)) : serialize(censor)
78
+ }
79
+
80
+ return [...Object.keys(shape), ...Object.getOwnPropertySymbols(shape)].reduce((o, k) => {
81
+ // top level key:
82
+ if (shape[k] === null) {
83
+ o[k] = (value) => topCensor(value, [k])
84
+ } else {
85
+ const wrappedCensor = typeof censor === 'function'
86
+ ? (value, path) => {
87
+ return censor(value, [k, ...path])
88
+ }
89
+ : censor
90
+ o[k] = Redact({
91
+ paths: shape[k],
92
+ censor: wrappedCensor,
93
+ serialize,
94
+ strict,
95
+ remove
96
+ })
97
+ }
98
+ return o
99
+ }, result)
100
+ }
101
+
102
+ function handle (opts) {
103
+ if (Array.isArray(opts)) {
104
+ opts = { paths: opts, censor: CENSOR }
105
+ return opts
106
+ }
107
+ let { paths, censor = CENSOR, remove } = opts
108
+ if (Array.isArray(paths) === false) { throw Error('pino – redact must contain an array of strings') }
109
+ if (remove === true) censor = undefined
110
+
111
+ return { paths, censor, remove }
112
+ }
113
+
114
+ module.exports = redaction
package/lib/symbols.js ADDED
@@ -0,0 +1,74 @@
1
+ 'use strict'
2
+
3
+ const setLevelSym = Symbol('pino.setLevel')
4
+ const getLevelSym = Symbol('pino.getLevel')
5
+ const levelValSym = Symbol('pino.levelVal')
6
+ const levelCompSym = Symbol('pino.levelComp')
7
+ const useLevelLabelsSym = Symbol('pino.useLevelLabels')
8
+ const useOnlyCustomLevelsSym = Symbol('pino.useOnlyCustomLevels')
9
+ const mixinSym = Symbol('pino.mixin')
10
+
11
+ const lsCacheSym = Symbol('pino.lsCache')
12
+ const chindingsSym = Symbol('pino.chindings')
13
+
14
+ const asJsonSym = Symbol('pino.asJson')
15
+ const writeSym = Symbol('pino.write')
16
+ const redactFmtSym = Symbol('pino.redactFmt')
17
+
18
+ const timeSym = Symbol('pino.time')
19
+ const timeSliceIndexSym = Symbol('pino.timeSliceIndex')
20
+ const streamSym = Symbol('pino.stream')
21
+ const stringifySym = Symbol('pino.stringify')
22
+ const stringifySafeSym = Symbol('pino.stringifySafe')
23
+ const stringifiersSym = Symbol('pino.stringifiers')
24
+ const endSym = Symbol('pino.end')
25
+ const formatOptsSym = Symbol('pino.formatOpts')
26
+ const messageKeySym = Symbol('pino.messageKey')
27
+ const errorKeySym = Symbol('pino.errorKey')
28
+ const nestedKeySym = Symbol('pino.nestedKey')
29
+ const nestedKeyStrSym = Symbol('pino.nestedKeyStr')
30
+ const mixinMergeStrategySym = Symbol('pino.mixinMergeStrategy')
31
+ const msgPrefixSym = Symbol('pino.msgPrefix')
32
+
33
+ const wildcardFirstSym = Symbol('pino.wildcardFirst')
34
+
35
+ // public symbols, no need to use the same pino
36
+ // version for these
37
+ const serializersSym = Symbol.for('pino.serializers')
38
+ const formattersSym = Symbol.for('pino.formatters')
39
+ const hooksSym = Symbol.for('pino.hooks')
40
+ const needsMetadataGsym = Symbol.for('pino.metadata')
41
+
42
+ module.exports = {
43
+ setLevelSym,
44
+ getLevelSym,
45
+ levelValSym,
46
+ levelCompSym,
47
+ useLevelLabelsSym,
48
+ mixinSym,
49
+ lsCacheSym,
50
+ chindingsSym,
51
+ asJsonSym,
52
+ writeSym,
53
+ serializersSym,
54
+ redactFmtSym,
55
+ timeSym,
56
+ timeSliceIndexSym,
57
+ streamSym,
58
+ stringifySym,
59
+ stringifySafeSym,
60
+ stringifiersSym,
61
+ endSym,
62
+ formatOptsSym,
63
+ messageKeySym,
64
+ errorKeySym,
65
+ nestedKeySym,
66
+ wildcardFirstSym,
67
+ needsMetadataGsym,
68
+ useOnlyCustomLevelsSym,
69
+ formattersSym,
70
+ hooksSym,
71
+ nestedKeyStrSym,
72
+ mixinMergeStrategySym,
73
+ msgPrefixSym
74
+ }
package/lib/time.js ADDED
@@ -0,0 +1,39 @@
1
+ 'use strict'
2
+
3
+ const nullTime = () => ''
4
+
5
+ const epochTime = () => `,"time":${Date.now()}`
6
+
7
+ const unixTime = () => `,"time":${Math.round(Date.now() / 1000.0)}`
8
+
9
+ const isoTime = () => `,"time":"${new Date(Date.now()).toISOString()}"` // using Date.now() for testability
10
+
11
+ const NS_PER_MS = 1_000_000n
12
+ const NS_PER_SEC = 1_000_000_000n
13
+
14
+ const startWallTimeNs = BigInt(Date.now()) * NS_PER_MS
15
+ const startHrTime = process.hrtime.bigint()
16
+
17
+ const isoTimeNano = () => {
18
+ const elapsedNs = process.hrtime.bigint() - startHrTime
19
+ const currentTimeNs = startWallTimeNs + elapsedNs
20
+
21
+ const secondsSinceEpoch = currentTimeNs / NS_PER_SEC
22
+ const nanosWithinSecond = currentTimeNs % NS_PER_SEC
23
+
24
+ const msSinceEpoch = Number(secondsSinceEpoch * 1000n + nanosWithinSecond / 1_000_000n)
25
+ const date = new Date(msSinceEpoch)
26
+
27
+ const year = date.getUTCFullYear()
28
+ const month = (date.getUTCMonth() + 1).toString().padStart(2, '0')
29
+ const day = date.getUTCDate().toString().padStart(2, '0')
30
+ const hours = date.getUTCHours().toString().padStart(2, '0')
31
+ const minutes = date.getUTCMinutes().toString().padStart(2, '0')
32
+ const seconds = date.getUTCSeconds().toString().padStart(2, '0')
33
+
34
+ return `,"time":"${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${nanosWithinSecond
35
+ .toString()
36
+ .padStart(9, '0')}Z"`
37
+ }
38
+
39
+ module.exports = { nullTime, epochTime, unixTime, isoTime, isoTimeNano }
package/lib/tools.js ADDED
@@ -0,0 +1,427 @@
1
+ 'use strict'
2
+
3
+ /* eslint no-prototype-builtins: 0 */
4
+
5
+ const diagChan = require('node:diagnostics_channel')
6
+ const format = require('quick-format-unescaped')
7
+ const { mapHttpRequest, mapHttpResponse } = require('pino-std-serializers')
8
+ const SonicBoom = require('sonic-boom')
9
+ const onExit = require('on-exit-leak-free')
10
+ const {
11
+ lsCacheSym,
12
+ chindingsSym,
13
+ writeSym,
14
+ serializersSym,
15
+ formatOptsSym,
16
+ endSym,
17
+ stringifiersSym,
18
+ stringifySym,
19
+ stringifySafeSym,
20
+ wildcardFirstSym,
21
+ nestedKeySym,
22
+ formattersSym,
23
+ messageKeySym,
24
+ errorKeySym,
25
+ nestedKeyStrSym,
26
+ msgPrefixSym
27
+ } = require('./symbols')
28
+ const { isMainThread } = require('worker_threads')
29
+ const transport = require('./transport')
30
+ const [nodeMajor] = process.versions.node.split('.').map(v => Number(v))
31
+
32
+ const asJsonChan = diagChan.tracingChannel('pino_asJson')
33
+
34
+ // JSON.stringify is faster in node 25+.
35
+ const asString = nodeMajor >= 25 ? str => JSON.stringify(str) : _asString
36
+
37
+ function noop () {
38
+ }
39
+
40
+ function genLog (level, hook) {
41
+ if (!hook) return LOG
42
+
43
+ return function hookWrappedLog (...args) {
44
+ hook.call(this, args, LOG, level)
45
+ }
46
+
47
+ function LOG (o, ...n) {
48
+ if (typeof o === 'object') {
49
+ let msg = o
50
+ if (o !== null) {
51
+ if (o.method && o.headers && o.socket) {
52
+ o = mapHttpRequest(o)
53
+ } else if (typeof o.setHeader === 'function') {
54
+ o = mapHttpResponse(o)
55
+ }
56
+ }
57
+ let formatParams
58
+ if (msg === null && n.length === 0) {
59
+ formatParams = [null]
60
+ } else {
61
+ msg = n.shift()
62
+ formatParams = n
63
+ }
64
+ // We do not use a coercive check for `msg` as it is
65
+ // measurably slower than the explicit checks.
66
+ if (typeof this[msgPrefixSym] === 'string' && msg !== undefined && msg !== null) {
67
+ msg = this[msgPrefixSym] + msg
68
+ }
69
+ this[writeSym](o, format(msg, formatParams, this[formatOptsSym]), level)
70
+ } else {
71
+ let msg = o === undefined ? n.shift() : o
72
+
73
+ // We do not use a coercive check for `msg` as it is
74
+ // measurably slower than the explicit checks.
75
+ if (typeof this[msgPrefixSym] === 'string' && msg !== undefined && msg !== null) {
76
+ msg = this[msgPrefixSym] + msg
77
+ }
78
+ this[writeSym](null, format(msg, n, this[formatOptsSym]), level)
79
+ }
80
+ }
81
+ }
82
+
83
+ // magically escape strings for json
84
+ // relying on their charCodeAt
85
+ // everything below 32 needs JSON.stringify()
86
+ // 34 and 92 happens all the time, so we
87
+ // have a fast case for them
88
+ function _asString (str) {
89
+ let result = ''
90
+ let last = 0
91
+ let found = false
92
+ let point = 255
93
+ const l = str.length
94
+ if (l > 100) {
95
+ return JSON.stringify(str)
96
+ }
97
+ for (var i = 0; i < l && point >= 32; i++) {
98
+ point = str.charCodeAt(i)
99
+ if (point === 34 || point === 92) {
100
+ result += str.slice(last, i) + '\\'
101
+ last = i
102
+ found = true
103
+ }
104
+ }
105
+ if (!found) {
106
+ result = str
107
+ } else {
108
+ result += str.slice(last)
109
+ }
110
+ return point < 32 ? JSON.stringify(str) : '"' + result + '"'
111
+ }
112
+
113
+ /**
114
+ * `asJson` wraps `_asJson` in order to facilitate generating diagnostics.
115
+ *
116
+ * @param {object} obj The merging object passed to the log method.
117
+ * @param {string} msg The log message passed to the log method.
118
+ * @param {number} num The log level number.
119
+ * @param {number} time The log time in milliseconds.
120
+ *
121
+ * @returns {string}
122
+ */
123
+ function asJson (obj, msg, num, time) {
124
+ if (asJsonChan.hasSubscribers === false) {
125
+ return _asJson.call(this, obj, msg, num, time)
126
+ }
127
+
128
+ const store = { instance: this, arguments }
129
+ return asJsonChan.traceSync(_asJson, store, this, obj, msg, num, time)
130
+ }
131
+
132
+ /**
133
+ * `_asJson` parses all collected data and generates the finalized newline
134
+ * delimited JSON string.
135
+ *
136
+ * @param {object} obj The merging object passed to the log method.
137
+ * @param {string} msg The log message passed to the log method.
138
+ * @param {number} num The log level number.
139
+ * @param {number} time The log time in milliseconds.
140
+ *
141
+ * @returns {string} The finalized log string terminated with a newline.
142
+ * @private
143
+ */
144
+ function _asJson (obj, msg, num, time) {
145
+ const stringify = this[stringifySym]
146
+ const stringifySafe = this[stringifySafeSym]
147
+ const stringifiers = this[stringifiersSym]
148
+ const end = this[endSym]
149
+ const chindings = this[chindingsSym]
150
+ const serializers = this[serializersSym]
151
+ const formatters = this[formattersSym]
152
+ const messageKey = this[messageKeySym]
153
+ const errorKey = this[errorKeySym]
154
+ let data = this[lsCacheSym][num] + time
155
+
156
+ // we need the child bindings added to the output first so instance logged
157
+ // objects can take precedence when JSON.parse-ing the resulting log line
158
+ data = data + chindings
159
+
160
+ let value
161
+ if (formatters.log) {
162
+ obj = formatters.log(obj)
163
+ }
164
+ const wildcardStringifier = stringifiers[wildcardFirstSym]
165
+ let propStr = ''
166
+ for (const key in obj) {
167
+ value = obj[key]
168
+ if (Object.prototype.hasOwnProperty.call(obj, key) && value !== undefined) {
169
+ if (serializers[key]) {
170
+ value = serializers[key](value)
171
+ } else if (key === errorKey && serializers.err) {
172
+ value = serializers.err(value)
173
+ }
174
+
175
+ const stringifier = stringifiers[key] || wildcardStringifier
176
+
177
+ switch (typeof value) {
178
+ case 'undefined':
179
+ case 'function':
180
+ continue
181
+ case 'number':
182
+ /* eslint no-fallthrough: "off" */
183
+ if (Number.isFinite(value) === false) {
184
+ value = null
185
+ }
186
+ // this case explicitly falls through to the next one
187
+ case 'boolean':
188
+ if (stringifier) value = stringifier(value)
189
+ break
190
+ case 'string':
191
+ value = (stringifier || asString)(value)
192
+ break
193
+ default:
194
+ value = (stringifier || stringify)(value, stringifySafe)
195
+ }
196
+ if (value === undefined) continue
197
+ const strKey = asString(key)
198
+ propStr += ',' + strKey + ':' + value
199
+ }
200
+ }
201
+
202
+ let msgStr = ''
203
+ if (msg !== undefined) {
204
+ value = serializers[messageKey] ? serializers[messageKey](msg) : msg
205
+ const stringifier = stringifiers[messageKey] || wildcardStringifier
206
+
207
+ switch (typeof value) {
208
+ case 'function':
209
+ break
210
+ case 'number':
211
+ if (Number.isFinite(value) === false) {
212
+ value = null
213
+ }
214
+ // this case explicitly falls through to the next one
215
+ case 'boolean':
216
+ if (stringifier) value = stringifier(value)
217
+ msgStr = ',"' + messageKey + '":' + value
218
+ break
219
+ case 'string':
220
+ value = (stringifier || asString)(value)
221
+ msgStr = ',"' + messageKey + '":' + value
222
+ break
223
+ default:
224
+ value = (stringifier || stringify)(value, stringifySafe)
225
+ msgStr = ',"' + messageKey + '":' + value
226
+ }
227
+ }
228
+
229
+ if (this[nestedKeySym] && propStr) {
230
+ // place all the obj properties under the specified key
231
+ // the nested key is already formatted from the constructor
232
+ return data + this[nestedKeyStrSym] + propStr.slice(1) + '}' + msgStr + end
233
+ } else {
234
+ return data + propStr + msgStr + end
235
+ }
236
+ }
237
+
238
+ function asChindings (instance, bindings) {
239
+ let value
240
+ let data = instance[chindingsSym]
241
+ const stringify = instance[stringifySym]
242
+ const stringifySafe = instance[stringifySafeSym]
243
+ const stringifiers = instance[stringifiersSym]
244
+ const wildcardStringifier = stringifiers[wildcardFirstSym]
245
+ const serializers = instance[serializersSym]
246
+ const formatter = instance[formattersSym].bindings
247
+ bindings = formatter(bindings)
248
+
249
+ for (const key in bindings) {
250
+ value = bindings[key]
251
+ const valid = (key.length < 5 || (key !== 'level' &&
252
+ key !== 'serializers' &&
253
+ key !== 'formatters' &&
254
+ key !== 'customLevels')) &&
255
+ bindings.hasOwnProperty(key) &&
256
+ value !== undefined
257
+ if (valid === true) {
258
+ value = serializers[key] ? serializers[key](value) : value
259
+ value = (stringifiers[key] || wildcardStringifier || stringify)(value, stringifySafe)
260
+ if (value === undefined) continue
261
+ data += ',"' + key + '":' + value
262
+ }
263
+ }
264
+ return data
265
+ }
266
+
267
+ function hasBeenTampered (stream) {
268
+ return stream.write !== stream.constructor.prototype.write
269
+ }
270
+
271
+ function buildSafeSonicBoom (opts) {
272
+ const stream = new SonicBoom(opts)
273
+ stream.on('error', filterBrokenPipe)
274
+ // If we are sync: false, we must flush on exit
275
+ if (!opts.sync && isMainThread) {
276
+ onExit.register(stream, autoEnd)
277
+
278
+ stream.on('close', function () {
279
+ onExit.unregister(stream)
280
+ })
281
+ }
282
+ return stream
283
+
284
+ function filterBrokenPipe (err) {
285
+ // Impossible to replicate across all operating systems
286
+ /* istanbul ignore next */
287
+ if (err.code === 'EPIPE') {
288
+ // If we get EPIPE, we should stop logging here
289
+ // however we have no control to the consumer of
290
+ // SonicBoom, so we just overwrite the write method
291
+ stream.write = noop
292
+ stream.end = noop
293
+ stream.flushSync = noop
294
+ stream.destroy = noop
295
+ return
296
+ }
297
+ stream.removeListener('error', filterBrokenPipe)
298
+ stream.emit('error', err)
299
+ }
300
+ }
301
+
302
+ function autoEnd (stream, eventName) {
303
+ // This check is needed only on some platforms
304
+ /* istanbul ignore next */
305
+ if (stream.destroyed) {
306
+ return
307
+ }
308
+
309
+ if (eventName === 'beforeExit') {
310
+ // We still have an event loop, let's use it
311
+ stream.flush()
312
+ stream.on('drain', function () {
313
+ stream.end()
314
+ })
315
+ } else {
316
+ // For some reason istanbul is not detecting this, but it's there
317
+ /* istanbul ignore next */
318
+ // We do not have an event loop, so flush synchronously
319
+ stream.flushSync()
320
+ }
321
+ }
322
+
323
+ function createArgsNormalizer (defaultOptions) {
324
+ return function normalizeArgs (instance, caller, opts = {}, stream) {
325
+ // support stream as a string
326
+ if (typeof opts === 'string') {
327
+ stream = buildSafeSonicBoom({ dest: opts })
328
+ opts = {}
329
+ } else if (typeof stream === 'string') {
330
+ if (opts && opts.transport) {
331
+ throw Error('only one of option.transport or stream can be specified')
332
+ }
333
+ stream = buildSafeSonicBoom({ dest: stream })
334
+ } else if (opts instanceof SonicBoom || opts.writable || opts._writableState) {
335
+ stream = opts
336
+ opts = {}
337
+ } else if (opts.transport) {
338
+ if (opts.transport instanceof SonicBoom || opts.transport.writable || opts.transport._writableState) {
339
+ throw Error('option.transport do not allow stream, please pass to option directly. e.g. pino(transport)')
340
+ }
341
+ if (opts.transport.targets && opts.transport.targets.length && opts.formatters && typeof opts.formatters.level === 'function') {
342
+ throw Error('option.transport.targets do not allow custom level formatters')
343
+ }
344
+
345
+ let customLevels
346
+ if (opts.customLevels) {
347
+ customLevels = opts.useOnlyCustomLevels ? opts.customLevels : Object.assign({}, opts.levels, opts.customLevels)
348
+ }
349
+ stream = transport({ caller, ...opts.transport, levels: customLevels })
350
+ }
351
+ opts = Object.assign({}, defaultOptions, opts)
352
+ opts.serializers = Object.assign({}, defaultOptions.serializers, opts.serializers)
353
+ opts.formatters = Object.assign({}, defaultOptions.formatters, opts.formatters)
354
+
355
+ if (opts.prettyPrint) {
356
+ throw new Error('prettyPrint option is no longer supported, see the pino-pretty package (https://github.com/pinojs/pino-pretty)')
357
+ }
358
+
359
+ const { enabled, onChild } = opts
360
+ if (enabled === false) opts.level = 'silent'
361
+ if (!onChild) opts.onChild = noop
362
+ if (!stream) {
363
+ if (!hasBeenTampered(process.stdout)) {
364
+ // If process.stdout.fd is undefined, it means that we are running
365
+ // in a worker thread. Let's assume we are logging to file descriptor 1.
366
+ stream = buildSafeSonicBoom({ fd: process.stdout.fd || 1 })
367
+ } else {
368
+ stream = process.stdout
369
+ }
370
+ }
371
+ return { opts, stream }
372
+ }
373
+ }
374
+
375
+ function stringify (obj, stringifySafeFn) {
376
+ try {
377
+ return JSON.stringify(obj)
378
+ } catch (_) {
379
+ try {
380
+ const stringify = stringifySafeFn || this[stringifySafeSym]
381
+ return stringify(obj)
382
+ } catch (_) {
383
+ return '"[unable to serialize, circular reference is too complex to analyze]"'
384
+ }
385
+ }
386
+ }
387
+
388
+ function buildFormatters (level, bindings, log) {
389
+ return {
390
+ level,
391
+ bindings,
392
+ log
393
+ }
394
+ }
395
+
396
+ /**
397
+ * Convert a string integer file descriptor to a proper native integer
398
+ * file descriptor.
399
+ *
400
+ * @param {string} destination The file descriptor string to attempt to convert.
401
+ *
402
+ * @returns {Number}
403
+ */
404
+ function normalizeDestFileDescriptor (destination) {
405
+ const fd = Number(destination)
406
+ if (typeof destination === 'string' && Number.isFinite(fd)) {
407
+ return fd
408
+ }
409
+ // destination could be undefined if we are in a worker
410
+ if (destination === undefined) {
411
+ // This is stdout in UNIX systems
412
+ return 1
413
+ }
414
+ return destination
415
+ }
416
+
417
+ module.exports = {
418
+ noop,
419
+ buildSafeSonicBoom,
420
+ asChindings,
421
+ asJson,
422
+ genLog,
423
+ createArgsNormalizer,
424
+ stringify,
425
+ buildFormatters,
426
+ normalizeDestFileDescriptor
427
+ }