Skip to content

Commit 2e6d8f4

Browse files
committed
refactor(logger): improve console formatting, parse JSON payloads, and expand callsite details
Improve console readability by formatting primitive and object args separately, escaping console format specifiers, and detecting inline JSON payloads to show structured data. Also replace compact grouping/printCallsiteDetails with clearer grouping and expanded source info.
1 parent 2ad6d63 commit 2e6d8f4

1 file changed

Lines changed: 137 additions & 20 deletions

File tree

js/log_system/logger.js

Lines changed: 137 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -204,30 +204,29 @@ class Logger {
204204
? console.log.bind(console)
205205
: consoleFn;
206206
const normalizedCallsite = this.normalizeCallsite(callsite);
207-
const shouldGroupCallsite = this.shouldGroupCallsite(normalizedCallsite);
208-
const outputFn = shouldGroupCallsite
207+
const shouldGroupDetails = this.shouldGroupDetails(normalizedCallsite);
208+
const outputFn = shouldGroupDetails
209209
? console.groupCollapsed.bind(console)
210210
: consoleFn;
211211
const { root, detail } = this.splitModuleName(module);
212-
const message = this.stringifyArgs(args);
212+
const consoleArgs = this.formatConsoleArgs(args);
213213
const suffix = this.formatSuffix(detail, normalizedCallsite);
214+
const suffixArgs = suffix ? [suffix.trimStart()] : [];
214215
if (this.config.useColors && typeof consoleFn === 'function') {
215216
const color = COLORS[level] || '#000000';
216-
outputFn(
217-
`%c ${levelName.padEnd(LEVEL_WIDTH, ' ')} %c ${timestamp} %c %c ${root} %c %c ${message}%c${suffix}`,
218-
`background:${color};color:#fff;font-weight:bold;`,
219-
`background:${TIME_BG};color:#fff;font-weight:bold;`,
220-
`background:${color};color:${color};`,
221-
`background:${TIME_BG};color:#fff;font-weight:bold;`,
222-
`background:${color};color:${color};`,
223-
`color:${color};font-weight:bold;`,
224-
'',
225-
);
226-
this.printCallsiteDetails(detailFn, normalizedCallsite);
217+
outputFn(...this.formatStyledConsoleArgs({
218+
levelName,
219+
timestamp,
220+
root,
221+
args: consoleArgs,
222+
suffix: suffixArgs[0] || '',
223+
color
224+
}));
225+
this.printExpandedDetails(detailFn, normalizedCallsite);
227226
return;
228227
}
229-
outputFn(`${levelName.padEnd(LEVEL_WIDTH, ' ')} ${timestamp} ${root} ${message}${suffix}`);
230-
this.printCallsiteDetails(detailFn, normalizedCallsite);
228+
outputFn(`${levelName.padEnd(LEVEL_WIDTH, ' ')} ${timestamp} ${root}`, ...consoleArgs, ...suffixArgs);
229+
this.printExpandedDetails(detailFn, normalizedCallsite);
231230
}
232231

233232
splitModuleName(module) {
@@ -266,6 +265,75 @@ class Logger {
266265
}).join(' ');
267266
}
268267

268+
formatConsoleArgs(args = []) {
269+
return args.flatMap((arg) => this.formatConsoleArg(arg));
270+
}
271+
272+
formatConsoleArg(arg) {
273+
if (typeof arg === 'string') {
274+
const payload = this.parseJsonPayloadFromString(arg);
275+
if (payload) {
276+
return payload.prefix ? [payload.prefix, payload.value] : [payload.value];
277+
}
278+
}
279+
return [arg];
280+
}
281+
282+
formatStyledConsoleArgs({ levelName, timestamp, root, args = [], suffix = '', color }) {
283+
const outputArgs = [
284+
`%c ${levelName.padEnd(LEVEL_WIDTH, ' ')} %c ${timestamp} %c %c ${root} %c %c `,
285+
`background:${color};color:#fff;font-weight:bold;`,
286+
`background:${TIME_BG};color:#fff;font-weight:bold;`,
287+
`background:${color};color:${color};`,
288+
`background:${TIME_BG};color:#fff;font-weight:bold;`,
289+
`background:${color};color:${color};`,
290+
`color:${color};font-weight:bold;`
291+
];
292+
293+
let format = outputArgs[0];
294+
let hasContent = false;
295+
args.forEach((arg) => {
296+
if (hasContent) {
297+
format += ' ';
298+
}
299+
if (this.isExpandableConsoleArg(arg)) {
300+
format += '%o';
301+
outputArgs.push(arg);
302+
}
303+
else {
304+
format += this.escapeConsoleFormat(this.stringifyConsolePrimitive(arg));
305+
}
306+
hasContent = true;
307+
});
308+
309+
if (suffix) {
310+
if (hasContent) {
311+
format += ' ';
312+
}
313+
format += `%c${this.escapeConsoleFormat(suffix)}`;
314+
outputArgs.push('');
315+
}
316+
317+
outputArgs[0] = format;
318+
return outputArgs;
319+
}
320+
321+
isExpandableConsoleArg(arg) {
322+
return (typeof arg === 'object' && arg !== null && !(arg instanceof Error))
323+
|| typeof arg === 'function';
324+
}
325+
326+
stringifyConsolePrimitive(arg) {
327+
if (arg instanceof Error) {
328+
return arg.stack || arg.message;
329+
}
330+
return String(arg);
331+
}
332+
333+
escapeConsoleFormat(value) {
334+
return String(value).replace(/%/g, '%%');
335+
}
336+
269337
getCallsite() {
270338
const stack = new Error().stack;
271339
if (!stack) {
@@ -353,21 +421,70 @@ class Logger {
353421
return filename.replace(/\.(?:mjs|js|tsx?|jsx)$/i, '') || this.formatCallsite(callsite);
354422
}
355423

356-
shouldGroupCallsite(callsite) {
424+
shouldGroupDetails(callsite) {
357425
return this.config.compactCallsite !== false
358426
&& !!callsite?.full
359427
&& typeof console.groupCollapsed === 'function'
360428
&& typeof console.groupEnd === 'function';
361429
}
362430

363-
printCallsiteDetails(consoleFn, callsite) {
364-
if (!this.shouldGroupCallsite(callsite)) {
431+
printExpandedDetails(consoleFn, callsite) {
432+
if (!this.shouldGroupDetails(callsite)) {
365433
return;
366434
}
367-
consoleFn(`Source: ${callsite.full}`);
435+
if (callsite?.full) {
436+
consoleFn(`Source: ${callsite.full}`);
437+
}
368438
console.groupEnd();
369439
}
370440

441+
parseJsonPayloadFromString(value) {
442+
const parsed = this.parseJsonLikeString(value);
443+
if (parsed !== null) {
444+
return {
445+
prefix: '',
446+
value: parsed
447+
};
448+
}
449+
const start = this.findJsonPayloadStart(value);
450+
if (start <= 0) {
451+
return null;
452+
}
453+
const payload = this.parseJsonLikeString(value.slice(start));
454+
if (payload === null) {
455+
return null;
456+
}
457+
return {
458+
prefix: value.slice(0, start).trimEnd(),
459+
value: payload
460+
};
461+
}
462+
463+
findJsonPayloadStart(value) {
464+
const objectStart = value.indexOf('{');
465+
const arrayStart = value.indexOf('[');
466+
if (objectStart < 0) {
467+
return arrayStart;
468+
}
469+
if (arrayStart < 0) {
470+
return objectStart;
471+
}
472+
return Math.min(objectStart, arrayStart);
473+
}
474+
475+
parseJsonLikeString(value) {
476+
const trimmed = value.trim();
477+
if (!trimmed || !/^[{[]/.test(trimmed)) {
478+
return null;
479+
}
480+
try {
481+
return JSON.parse(trimmed);
482+
}
483+
catch (e) {
484+
return null;
485+
}
486+
}
487+
371488
formatSuffix(detail, callsite, options = {}) {
372489
const normalizedCallsite = this.normalizeCallsite(callsite);
373490
const compactCallsite = options.compactCallsite ?? this.config.compactCallsite !== false;

0 commit comments

Comments
 (0)