Skip to content

Commit ae14cba

Browse files
committed
chore: added positional formatting
1 parent 2931365 commit ae14cba

File tree

1 file changed

+70
-39
lines changed

1 file changed

+70
-39
lines changed

src/logger.ts

Lines changed: 70 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ const terminalStyles = {
4040
// Log level icons/badges similar to consola
4141
const levelIcons = {
4242
debug: '🔍',
43-
info: 'ℹ',
44-
success: '✓',
43+
info: terminalStyles.green('ℹ'),
44+
success: terminalStyles.green('✓'),
4545
warning: terminalStyles.yellow('WARN'), // Use badge style for warnings
4646
error: terminalStyles.red('ERROR'), // Use badge style for errors
4747
}
@@ -100,6 +100,7 @@ export class Logger {
100100
private fancy: boolean // Whether to use fancy terminal output
101101
private tagFormat: TagFormat
102102
private timestampPosition: 'left' | 'right'
103+
private readonly ANSI_PATTERN = /\u001B\[\d+m/g
103104

104105
constructor(name: string, options: Partial<ExtendedLoggerOptions> = {}) {
105106
this.name = name
@@ -791,12 +792,29 @@ export class Logger {
791792
}
792793

793794
private formatConsoleTimestamp(date: Date): string {
794-
return terminalStyles.gray(date.toLocaleTimeString())
795+
return this.fancy ? terminalStyles.gray(date.toLocaleTimeString()) : date.toLocaleTimeString()
795796
}
796797

797798
private formatConsoleMessage(parts: { timestamp: string, icon?: string, tag?: string, message: string, level?: LogLevel, showTimestamp?: boolean }): string {
798799
const { timestamp, icon = '', tag = '', message, level, showTimestamp = true } = parts
799800

801+
// If fancy mode is disabled, return a simple format
802+
if (!this.fancy) {
803+
const components = []
804+
if (showTimestamp)
805+
components.push(timestamp)
806+
if (level === 'warning')
807+
components.push('WARN')
808+
else if (level === 'error')
809+
components.push('ERROR')
810+
else if (icon)
811+
components.push(icon.replace(/[^\p{L}\p{N}\p{P}\p{Z}]/gu, ''))
812+
if (tag)
813+
components.push(tag.replace(/[[\]]/g, ''))
814+
components.push(message)
815+
return components.join(' ')
816+
}
817+
800818
// Get terminal width, default to 120 if not available
801819
const terminalWidth = process.stdout.columns || 120
802820

@@ -806,10 +824,14 @@ export class Logger {
806824
// For warning and error, show badge-style level indicator
807825
mainPart = `${icon} ${message}`
808826
}
809-
else {
810-
// For other levels, show icon followed by message
827+
else if (level === 'info' || level === 'success') {
828+
// For info and success, keep icon colored but message plain
811829
mainPart = `${icon} ${tag} ${message}`
812830
}
831+
else {
832+
// For other levels, apply standard formatting
833+
mainPart = `${icon} ${tag} ${terminalStyles.cyan(message)}`
834+
}
813835

814836
// If we don't need to show timestamp, just return the message
815837
if (!showTimestamp) {
@@ -821,6 +843,48 @@ export class Logger {
821843
return `${mainPart.trim()}${' '.repeat(padding)}${timestamp}`
822844
}
823845

846+
private formatMessage(message: string, args: any[]): string {
847+
// If the last argument is an array, use positional {n} formatting
848+
if (args.length === 1 && Array.isArray(args[0])) {
849+
return message.replace(/\{(\d+)\}/g, (match, index) => {
850+
const position = Number.parseInt(index, 10)
851+
return position < args[0].length ? String(args[0][position]) : match
852+
})
853+
}
854+
855+
// Otherwise use the existing %s style formatting
856+
const formatRegex = /%([sdijfo%])/g
857+
let argIndex = 0
858+
let formattedMessage = message.replace(formatRegex, (match, type) => {
859+
if (type === '%')
860+
return '%'
861+
if (argIndex >= args.length)
862+
return match
863+
const arg = args[argIndex++]
864+
switch (type) {
865+
case 's':
866+
return String(arg)
867+
case 'd':
868+
case 'i':
869+
return Number(arg).toString()
870+
case 'j':
871+
case 'o':
872+
return JSON.stringify(arg, null, 2)
873+
default:
874+
return match
875+
}
876+
})
877+
878+
// Append any remaining args
879+
if (argIndex < args.length) {
880+
formattedMessage += ` ${args.slice(argIndex).map(arg =>
881+
typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg),
882+
).join(' ')}`
883+
}
884+
885+
return formattedMessage
886+
}
887+
824888
private async log(level: LogLevel, message: string | Error, ...args: any[]): Promise<void> {
825889
if (!this.shouldLog(level))
826890
return
@@ -838,40 +902,7 @@ export class Logger {
838902
errorStack = message.stack
839903
}
840904
else {
841-
formattedMessage = message
842-
}
843-
844-
// Format the message with format strings if there are args
845-
if (args && args.length > 0) {
846-
// Handle format strings
847-
const formatRegex = /%([sdijfo%])/g
848-
let argIndex = 0
849-
formattedMessage = formattedMessage.replace(formatRegex, (match, type) => {
850-
if (type === '%')
851-
return '%'
852-
if (argIndex >= args.length)
853-
return match
854-
const arg = args[argIndex++]
855-
switch (type) {
856-
case 's':
857-
return String(arg)
858-
case 'd':
859-
case 'i':
860-
return Number(arg).toString()
861-
case 'j':
862-
case 'o':
863-
return JSON.stringify(arg, null, 2)
864-
default:
865-
return match
866-
}
867-
})
868-
869-
// Append any remaining args
870-
if (argIndex < args.length) {
871-
formattedMessage += ` ${args.slice(argIndex).map(arg =>
872-
typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg),
873-
).join(' ')}`
874-
}
905+
formattedMessage = this.formatMessage(message, args)
875906
}
876907

877908
// Format console output

0 commit comments

Comments
 (0)