@reyalp/debug-utils
Version:
ts transformers for debug
129 lines (98 loc) • 4.82 kB
text/typescript
import strftime from 'strftime'
import { inspect } from 'util'
import { _not_transformed } from './prelude'
export enum LogLevel { ERROR, WARN, INFO, VERBOSE, DEBUG, SILLY }
export interface LogCallChain1 {
error (...messages: unknown[]): LogCallChain2
warn (...messages: unknown[]): LogCallChain2
info (...messages: unknown[]): LogCallChain2
verbose (...messages: unknown[]): LogCallChain2
debug (...messages: unknown[]): LogCallChain2
silly (...messages: unknown[]): LogCallChain2
}
export interface LogCallChain2 {
trace (enabled: boolean): void
msg (...messages: unknown[]): LogCallChain2
dump (...exprs: unknown[]): LogCallChain2
}
export class Logger implements LogCallChain1 {
readonly rt: LogRuntime
private constructor(readonly tags: string[], impls: Partial<LogImpls>) {
this.rt = { LogLevel, ...LoggerDefaults, ...impls }
}
tag (...tags: string[]): LogCallChain1 { void tags; return _not_transformed() }
dump (...exprs: unknown[]): LogCallChain2 { void exprs; return _not_transformed() }
error (...messages: unknown[]): LogCallChain2 { void messages; return _not_transformed() }
warn (...messages: unknown[]): LogCallChain2 { void messages; return _not_transformed() }
info (...messages: unknown[]): LogCallChain2 { void messages; return _not_transformed() }
verbose (...messages: unknown[]): LogCallChain2 { void messages; return _not_transformed() }
debug (...messages: unknown[]): LogCallChain2 { void messages; return _not_transformed() }
silly (...messages: unknown[]): LogCallChain2 { void messages; return _not_transformed() }
extend(...args: [...tags: string[], impls: Partial<LogImpls>] | string[]) {
const [tags, impls] =
args.length === 0 ? [[], {}]
: typeof args.at(-1) === 'string' ? [args as string[], {}]
: [args.slice(0, -1) as string[], args.at(-1) as Partial<LogImpls>]
return new Logger([...new Set(this.tags.concat(tags))], { ...this.rt, ...impls })
}
static create(...args: [...tags: string[], impls: Partial<LogImpls>] | string[]) {
const [tags, impls] =
args.length === 0 ? [[], {}]
: typeof args.at(-1) === 'string' ? [args as string[], {}]
: [args.slice(0, -1) as string[], args.at(-1) as Partial<LogImpls>]
return new Logger([...new Set(tags)], impls)
}
}
type Location = [file: string, line: number, name: string]
type LogArgs = [ll: LogLevel, tags: string[], runtime: LogRuntime, ...Location]
export type LogFilter = (ll: LogLevel, tags: string[], runtime: LogRuntime) => boolean
export type LogPrinter = (obj: unknown, hint: string | undefined, runtime: LogRuntime) => string
export type LogWriter = (lines: string[], ...args: LogArgs) => void
export type LogFormatter = (messages: string[], ...args: LogArgs) => string[]
export const DefaultLogFormatter: LogFormatter = (messages, ll, tags, _rt, file, line, name) => {
const where = file ? ` ${file}:${line} ${name}` : ''
const label = `${strftime('%m-%d %T.%L')} ${'EWIVDS'[ll]}${tags.length ? (' (' + tags.join('|') + ')') : ''}${where}`
const lines = messages.join(' ').split('\n').map(s => s.trimEnd())
if (lines.length === 0) return [label + ':']
if (lines.length === 1) return [label + ': ' + lines[0]]
const indent = ' '.repeat(label.length)
return lines.map((s, i) => (i ? indent : label) + (i ? '| ' : ': ') + s)
}
export const DefaultLogWriter: LogWriter = lines => {
for (const line of lines) console.log(line)
}
export const DefaultLogFilterConfigs = {
level: LogLevel.DEBUG,
masks: {} as Record<string, boolean>
}
const min = LogLevel.ERROR
const max = LogLevel.SILLY
export function parseLogLevel(l: number | string): LogLevel | undefined {
if (typeof l === 'number') {
return l >= min && l <= max ? l : undefined
} else {
const name = l.toUpperCase()
return (LogLevel as unknown as Record<string, LogLevel>)[name]
}
}
export const DefaultLogFilter: LogFilter = (ll, tags) => {
if (ll > DefaultLogFilterConfigs.level) return false
if (tags.length === 0) return true
for (const t of tags) {
if (DefaultLogFilterConfigs.masks[t]) {
return false
}
}
return true
}
const _inspect = (obj: any) => inspect(obj, false, null, false)
export const DefaultLogPrinter: LogPrinter = (obj, hint) => hint ? _inspect(obj) : (inspect(obj) + ` ::${hint}`)
export type LogImpls = typeof LoggerDefaults
export const LoggerDefaults = {
filter: DefaultLogFilter,
format: DefaultLogFormatter,
inspect: DefaultLogPrinter,
write: DefaultLogWriter,
}
export type LogRuntime = typeof runtime
const runtime = { LogLevel, ...LoggerDefaults }