Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | import type { TransformableInfo, TransformFunction } from '../type' import util from 'node:util' import { SPLAT } from '../../triple-beam' /** * 捕获给定字符串中的格式(即 %s 字符串)的数量。 * 基于 `util.format`,参见 Node.js 源代码: * https://github.com/nodejs/node/blob/b1c8f15c5f169e021f7c46eb7b219de95fe97603/lib/util.js#L201-L230 */ const formatRegExp = /%[scdjifoO%]/g /** * 捕获格式字符串中转义的 % 符号的数量(即 %s 字符串)。 */ const escapedPercent = /%%/g class Splatter { constructor() {} /** * 检查 tokens <= splat.length,将 { splat, meta } 分配到 `info` 中,并写入此实例。 * * @param info Logform 信息消息。 * @param tokens 一组字符串插值标记。 * @returns 修改后的信息消息 * @private */ private _splat(info: TransformableInfo, tokens: string[]) { const msg = info.message || '' const splat = info[SPLAT] || info.splat || [] const percents = msg.match(escapedPercent) const escapes = percents ? percents.length : 0 // 预期的 splat 是标记的数量减去转义的数量 // 例如: // - { expectedSplat: 3 } '%d %s %j' // - { expectedSplat: 5 } '[%s] %d%% %d%% %s %j' // // 任何 "meta" 都将是预期 splat 大小之外的参数,无论类型如何。例如: // // logger.log('info', '%d%% %s %j', 100, 'wow', { such: 'js' }, { thisIsMeta: true }); // 将导致 splat 为四(4),但预期只有三(3)。因此: // // extraSplat = 3 - 4 = -1 // metas = [100, 'wow', { such: 'js' }, { thisIsMeta: true }].splice(-1, -1 * -1); // splat = [100, 'wow', { such: 'js' }] const expectedSplat = tokens.length - escapes const extraSplat = expectedSplat - splat.length const metas = extraSplat < 0 ? splat.splice(extraSplat, -1 * extraSplat) : [] // 现在 { splat } 已经与任何潜在的 { meta } 分开。我们 // 可以将其分配给 `info` 对象并将其写入我们的格式流。 // 如果额外的 metas 不是对象或缺少可枚举属性 // 你将会遇到麻烦。 const metalen = metas.length if (metalen) { for (let i = 0; i < metalen; i++) { Object.assign(info, metas[i]) } } info.message = util.format(msg, ...splat) return info } /** * 使用 `util.format` 完成 `info.message` 提供的 `info` 消息。 * 如果没有标记,则 `info` 是不可变的。 * * @param info Logform 信息消息。 * @returns 修改后的信息消息 */ transform: TransformFunction = (info) => { const msg = info.message || '' const splat = info[SPLAT] || info.splat // 如果 splat 未定义,则无需处理任何内容 if (!splat || !splat.length) { return info } // 提取标记,如果没有可用的标记,则默认为空数组以 // 确保预期结果的一致性 const tokens = msg.match(formatRegExp) // 此条件将处理具有 info[SPLAT] // 但没有标记存在的输入 if (!tokens && (splat || splat.length)) { const metas = splat.length > 1 ? splat.splice(0) : splat // 现在 { splat } 已经与任何潜在的 { meta } 分开。我们 // 可以将其分配给 `info` 对象并将其写入我们的格式流。 // 如果额外的 metas 不是对象或缺少可枚举属性 // 你将会遇到麻烦。 const metalen = metas.length if (metalen) { for (let i = 0; i < metalen; i++) { Object.assign(info, metas[i]) } } return info } if (tokens) { return this._splat(info, tokens) } return info } } /* * function splat (info) * 返回一个新的 splat 格式 TransformStream 实例 * 它从 `info` 对象执行字符串插值。这之前 * 在 `winston < 3.0.0` 中隐式暴露。 */ export default () => new Splatter() |