@tryghost/pretty-stream 2.0.2 → 2.1.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.
- package/README.md +9 -14
- package/lib/PrettyStream.js +104 -80
- package/package.json +15 -15
package/README.md
CHANGED
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
or
|
|
8
8
|
|
|
9
|
-
`
|
|
10
|
-
|
|
9
|
+
`pnpm add @tryghost/pretty-stream`
|
|
11
10
|
|
|
12
11
|
## Purpose
|
|
13
12
|
|
|
@@ -19,26 +18,22 @@ Used by `@tryghost/logging` and `@tryghost/metrics`.
|
|
|
19
18
|
|
|
20
19
|
## Develop
|
|
21
20
|
|
|
22
|
-
This is a mono repository, managed with [
|
|
21
|
+
This is a mono repository, managed with [Nx](https://nx.dev).
|
|
23
22
|
|
|
24
23
|
Follow the instructions for the top-level repo.
|
|
25
|
-
1. `git clone` this repo & `cd` into it as usual
|
|
26
|
-
2. Run `yarn` to install top-level dependencies.
|
|
27
24
|
|
|
25
|
+
1. `git clone` this repo & `cd` into it as usual
|
|
26
|
+
2. Run `pnpm install` to install top-level dependencies.
|
|
28
27
|
|
|
29
28
|
## Run
|
|
30
29
|
|
|
31
|
-
- `
|
|
32
|
-
|
|
30
|
+
- `pnpm dev`
|
|
33
31
|
|
|
34
32
|
## Test
|
|
35
33
|
|
|
36
|
-
- `
|
|
37
|
-
- `
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
- `pnpm lint` runs oxlint
|
|
35
|
+
- `pnpm test` runs lint and tests
|
|
41
36
|
|
|
42
|
-
# Copyright & License
|
|
37
|
+
# Copyright & License
|
|
43
38
|
|
|
44
|
-
Copyright (c) 2013-2026 Ghost Foundation - Released under the [MIT license](LICENSE).
|
|
39
|
+
Copyright (c) 2013-2026 Ghost Foundation - Released under the [MIT license](LICENSE).
|
package/lib/PrettyStream.js
CHANGED
|
@@ -17,7 +17,7 @@ const _private = {
|
|
|
17
17
|
30: 'info',
|
|
18
18
|
40: 'warn',
|
|
19
19
|
50: 'error',
|
|
20
|
-
60: 'fatal'
|
|
20
|
+
60: 'fatal',
|
|
21
21
|
},
|
|
22
22
|
colorForLevel: {
|
|
23
23
|
10: 'grey',
|
|
@@ -25,7 +25,7 @@ const _private = {
|
|
|
25
25
|
30: 'cyan',
|
|
26
26
|
40: 'magenta',
|
|
27
27
|
50: 'red',
|
|
28
|
-
60: 'inverse'
|
|
28
|
+
60: 'inverse',
|
|
29
29
|
},
|
|
30
30
|
colors: {
|
|
31
31
|
default: [39, 39],
|
|
@@ -41,26 +41,37 @@ const _private = {
|
|
|
41
41
|
green: [32, 39],
|
|
42
42
|
magenta: [35, 39],
|
|
43
43
|
red: [31, 39],
|
|
44
|
-
yellow: [33, 39]
|
|
45
|
-
}
|
|
44
|
+
yellow: [33, 39],
|
|
45
|
+
},
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
function colorize(colors, value) {
|
|
49
49
|
if (isArray(colors)) {
|
|
50
50
|
return colors.reduce((acc, color) => colorize(color, acc), value);
|
|
51
51
|
} else {
|
|
52
|
-
return
|
|
52
|
+
return (
|
|
53
|
+
'\x1B[' +
|
|
54
|
+
_private.colors[colors][0] +
|
|
55
|
+
'm' +
|
|
56
|
+
value +
|
|
57
|
+
'\x1B[' +
|
|
58
|
+
_private.colors[colors][1] +
|
|
59
|
+
'm'
|
|
60
|
+
);
|
|
53
61
|
}
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
function statusCode(status) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
const color =
|
|
66
|
+
status >= 500
|
|
67
|
+
? 'red'
|
|
68
|
+
: status >= 400
|
|
69
|
+
? 'yellow'
|
|
70
|
+
: status >= 300
|
|
71
|
+
? 'cyan'
|
|
72
|
+
: status >= 200
|
|
73
|
+
? 'green'
|
|
74
|
+
: 'default'; // no color
|
|
64
75
|
|
|
65
76
|
return colorize(color, status);
|
|
66
77
|
}
|
|
@@ -106,7 +117,10 @@ class PrettyStream extends Transform {
|
|
|
106
117
|
|
|
107
118
|
if (data.time) {
|
|
108
119
|
// If time is provided as a string in the expected format, use it directly
|
|
109
|
-
if (
|
|
120
|
+
if (
|
|
121
|
+
typeof data.time === 'string' &&
|
|
122
|
+
/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(data.time)
|
|
123
|
+
) {
|
|
110
124
|
time = data.time;
|
|
111
125
|
} else {
|
|
112
126
|
// Otherwise, parse and format it
|
|
@@ -126,99 +140,109 @@ class PrettyStream extends Transform {
|
|
|
126
140
|
logLevel = '\x1B[' + codes[0] + 'm' + logLevel + '\x1B[' + codes[1] + 'm';
|
|
127
141
|
|
|
128
142
|
if (data.req) {
|
|
129
|
-
output += format(
|
|
143
|
+
output += format(
|
|
144
|
+
'[%s] %s "%s %s" %s %s\n',
|
|
130
145
|
time,
|
|
131
146
|
logLevel,
|
|
132
147
|
data.req.method.toUpperCase(),
|
|
133
148
|
get(data, 'req.originalUrl'),
|
|
134
149
|
statusCode(get(data, 'res.statusCode')),
|
|
135
|
-
get(data, 'res.responseTime')
|
|
150
|
+
get(data, 'res.responseTime'),
|
|
136
151
|
);
|
|
137
152
|
} else if (data.msg === undefined) {
|
|
138
|
-
output += format('[%s] %s\n',
|
|
139
|
-
time,
|
|
140
|
-
logLevel
|
|
141
|
-
);
|
|
153
|
+
output += format('[%s] %s\n', time, logLevel);
|
|
142
154
|
} else {
|
|
143
155
|
bodyPretty += data.msg;
|
|
144
156
|
output += format('[%s] %s %s\n', time, logLevel, bodyPretty);
|
|
145
157
|
}
|
|
146
158
|
|
|
147
|
-
each(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (value.help) {
|
|
163
|
-
error += colorize('yellow', value.help) + '\n';
|
|
164
|
-
}
|
|
159
|
+
each(
|
|
160
|
+
Object.fromEntries(Object.entries(data).filter(([key]) => !OMITTED_KEYS.includes(key))),
|
|
161
|
+
function (value, key) {
|
|
162
|
+
// we always output errors for now
|
|
163
|
+
if (isObject(value) && value.message && value.stack) {
|
|
164
|
+
let error = '\n';
|
|
165
|
+
|
|
166
|
+
if (value.errorType) {
|
|
167
|
+
error +=
|
|
168
|
+
colorize(
|
|
169
|
+
_private.colorForLevel[data.level],
|
|
170
|
+
'Type: ' + value.errorType,
|
|
171
|
+
) + '\n';
|
|
172
|
+
}
|
|
165
173
|
|
|
166
|
-
|
|
167
|
-
error += '\n';
|
|
168
|
-
}
|
|
174
|
+
error += colorize(_private.colorForLevel[data.level], value.message) + '\n\n';
|
|
169
175
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
176
|
+
if (value.context) {
|
|
177
|
+
error += colorize('white', value.context) + '\n';
|
|
178
|
+
}
|
|
174
179
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
180
|
+
if (value.help) {
|
|
181
|
+
error += colorize('yellow', value.help) + '\n';
|
|
182
|
+
}
|
|
179
183
|
|
|
180
|
-
|
|
181
|
-
|
|
184
|
+
if (value.context || value.help) {
|
|
185
|
+
error += '\n';
|
|
186
|
+
}
|
|
182
187
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
} catch (err) {
|
|
187
|
-
// no need for special handling as we default to unparsed 'errorDetails'
|
|
188
|
+
if (value.id) {
|
|
189
|
+
error += colorize(['white', 'bold'], 'Error ID:') + '\n';
|
|
190
|
+
error += ' ' + colorize('grey', value.id) + '\n\n';
|
|
188
191
|
}
|
|
189
192
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
+
if (value.code) {
|
|
194
|
+
error += colorize(['white', 'bold'], 'Error Code: ') + '\n';
|
|
195
|
+
error += ' ' + colorize('grey', value.code) + '\n\n';
|
|
196
|
+
}
|
|
193
197
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
198
|
+
if (value.errorDetails) {
|
|
199
|
+
let details = value.errorDetails;
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
const jsonDetails = JSON.parse(value.errorDetails);
|
|
203
|
+
details = isArray(jsonDetails) ? jsonDetails[0] : jsonDetails;
|
|
204
|
+
} catch {
|
|
205
|
+
// no need for special handling as we default to unparsed 'errorDetails'
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const pretty = prettyjson.render(
|
|
209
|
+
details,
|
|
210
|
+
{
|
|
211
|
+
noColor: true,
|
|
212
|
+
},
|
|
213
|
+
4,
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
error += colorize(['white', 'bold'], 'Details:') + '\n';
|
|
217
|
+
error += colorize('grey', pretty) + '\n\n';
|
|
218
|
+
}
|
|
197
219
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
220
|
+
if (value.stack && !value.hideStack) {
|
|
221
|
+
error +=
|
|
222
|
+
colorize('grey', '----------------------------------------') + '\n\n';
|
|
223
|
+
error += colorize('grey', value.stack) + '\n';
|
|
224
|
+
}
|
|
202
225
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
226
|
+
output += format('%s\n', colorize(_private.colorForLevel[data.level], error));
|
|
227
|
+
} else if (isObject(value)) {
|
|
228
|
+
bodyPretty += '\n' + colorize('yellow', key.toUpperCase()) + '\n';
|
|
206
229
|
|
|
207
|
-
|
|
230
|
+
let sanitized = {};
|
|
208
231
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
232
|
+
each(value, function (innerValue, innerKey) {
|
|
233
|
+
if (!isEmpty(innerValue)) {
|
|
234
|
+
sanitized[innerKey] = innerValue;
|
|
235
|
+
}
|
|
236
|
+
});
|
|
214
237
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
238
|
+
bodyPretty += prettyjson.render(sanitized, {}) + '\n';
|
|
239
|
+
} else {
|
|
240
|
+
bodyPretty += prettyjson.render(value, {}) + '\n';
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
);
|
|
220
244
|
|
|
221
|
-
if (this.mode !== 'short' &&
|
|
245
|
+
if (this.mode !== 'short' && bodyPretty !== data.msg) {
|
|
222
246
|
output += format('%s\n', colorize('grey', bodyPretty));
|
|
223
247
|
}
|
|
224
248
|
|
package/package.json
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tryghost/pretty-stream",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"author": "Ghost Foundation",
|
|
4
6
|
"repository": {
|
|
5
7
|
"type": "git",
|
|
6
8
|
"url": "git+https://github.com/TryGhost/framework.git",
|
|
7
9
|
"directory": "packages/pretty-stream"
|
|
8
10
|
},
|
|
9
|
-
"author": "Ghost Foundation",
|
|
10
|
-
"license": "MIT",
|
|
11
|
-
"main": "index.js",
|
|
12
|
-
"scripts": {
|
|
13
|
-
"dev": "echo \"Implement me!\"",
|
|
14
|
-
"test": "NODE_ENV=testing vitest run --coverage --config ../../vitest.config.ts",
|
|
15
|
-
"lint": "eslint . --ext .js --cache",
|
|
16
|
-
"posttest": "yarn lint"
|
|
17
|
-
},
|
|
18
11
|
"files": [
|
|
19
12
|
"index.js",
|
|
20
13
|
"lib"
|
|
21
14
|
],
|
|
15
|
+
"main": "index.js",
|
|
22
16
|
"publishConfig": {
|
|
23
17
|
"access": "public"
|
|
24
18
|
},
|
|
25
|
-
"devDependencies": {
|
|
26
|
-
"sinon": "21.0.2"
|
|
27
|
-
},
|
|
28
19
|
"dependencies": {
|
|
29
20
|
"date-format": "4.0.14",
|
|
30
|
-
"lodash": "4.
|
|
21
|
+
"lodash": "4.18.1",
|
|
31
22
|
"prettyjson": "1.2.5"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"sinon": "21.1.2"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"dev": "echo \"Implement me!\"",
|
|
29
|
+
"test": "NODE_ENV=testing vitest run --coverage --config ../../vitest.config.ts",
|
|
30
|
+
"lint": "oxlint -c ../../.oxlintrc.json .",
|
|
31
|
+
"posttest": "pnpm run lint"
|
|
32
32
|
}
|
|
33
|
-
}
|
|
33
|
+
}
|