@innei/pretty-logger-core 0.3.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/consola/consola.ts +409 -0
- package/consola/constants.ts +109 -0
- package/consola/core.ts +2 -0
- package/consola/index.ts +50 -0
- package/consola/reporters/basic.ts +74 -0
- package/consola/reporters/browser.ts +70 -0
- package/consola/reporters/fancy.ts +152 -0
- package/consola/reporters/file.ts +126 -0
- package/consola/reporters/index.ts +6 -0
- package/consola/reporters/logger.ts +31 -0
- package/consola/reporters/subscriber.ts +30 -0
- package/consola/shared.ts +6 -0
- package/consola/types.ts +60 -0
- package/consola/utils/box.ts +320 -0
- package/consola/utils/color.ts +132 -0
- package/consola/utils/error.ts +12 -0
- package/consola/utils/log.ts +22 -0
- package/consola/utils/stream.ts +9 -0
- package/consola/utils/string.ts +64 -0
- package/consola/utils/tester.ts +16 -0
- package/consola/utils.ts +9 -0
- package/consola.instance.ts +36 -0
- package/dist/index.d.mts +190 -0
- package/dist/index.d.ts +190 -0
- package/dist/index.js +1105 -0
- package/dist/index.mjs +1054 -0
- package/index.ts +3 -0
- package/package.json +23 -0
- package/tool.util.ts +29 -0
- package/tsup.config.ts +9 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import _stringWidth from 'string-width'
|
|
2
|
+
import type { LogLevel, LogType } from '../constants'
|
|
3
|
+
import type { FormatOptions, LogObject } from '../types'
|
|
4
|
+
import type { BoxOpts } from '../utils/box'
|
|
5
|
+
|
|
6
|
+
import { stripAnsi } from '../utils'
|
|
7
|
+
import { box } from '../utils/box'
|
|
8
|
+
import { colors } from '../utils/color'
|
|
9
|
+
import { parseStack } from '../utils/error'
|
|
10
|
+
import { isUnicodeSupported } from '../utils/tester'
|
|
11
|
+
import { BasicReporter } from './basic'
|
|
12
|
+
|
|
13
|
+
export const TYPE_COLOR_MAP: { [k in LogType]?: string } = {
|
|
14
|
+
info: 'cyan',
|
|
15
|
+
fail: 'red',
|
|
16
|
+
success: 'green',
|
|
17
|
+
ready: 'green',
|
|
18
|
+
start: 'magenta',
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const LEVEL_COLOR_MAP: { [k in LogLevel]?: string } = {
|
|
22
|
+
0: 'red',
|
|
23
|
+
1: 'yellow',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const unicode = isUnicodeSupported()
|
|
27
|
+
const s = (c: string, fallback: string) => (unicode ? c : fallback)
|
|
28
|
+
const TYPE_ICONS: { [k in LogType]?: string } = {
|
|
29
|
+
error: s('✖', '×'),
|
|
30
|
+
fatal: s('✖', '×'),
|
|
31
|
+
ready: s('✔', '√'),
|
|
32
|
+
warn: s('⚠', '‼'),
|
|
33
|
+
info: s('ℹ', 'i'),
|
|
34
|
+
success: s('✔', '√'),
|
|
35
|
+
debug: s('⚙', 'D'),
|
|
36
|
+
trace: s('→', '→'),
|
|
37
|
+
fail: s('✖', '×'),
|
|
38
|
+
start: s('◐', 'o'),
|
|
39
|
+
log: '',
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function stringWidth(str: string) {
|
|
43
|
+
// https://github.com/unjs/consola/issues/204
|
|
44
|
+
if (!(Intl as any).Segmenter) {
|
|
45
|
+
return stripAnsi(str).length
|
|
46
|
+
}
|
|
47
|
+
return _stringWidth(str)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export class FancyReporter extends BasicReporter {
|
|
51
|
+
formatStack(stack: string) {
|
|
52
|
+
return `\n${parseStack(stack)
|
|
53
|
+
.map(
|
|
54
|
+
(line) =>
|
|
55
|
+
` ${line
|
|
56
|
+
.replace(/^at +/, (m) => colors.gray(m))
|
|
57
|
+
.replace(/\((.+)\)/, (_, m) => `(${colors.cyan(m)})`)}`,
|
|
58
|
+
)
|
|
59
|
+
.join('\n')}`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
formatType(logObj: LogObject, isBadge: boolean, opts: FormatOptions) {
|
|
63
|
+
const typeColor =
|
|
64
|
+
(TYPE_COLOR_MAP as any)[logObj.type] ||
|
|
65
|
+
(LEVEL_COLOR_MAP as any)[logObj.level] ||
|
|
66
|
+
'gray'
|
|
67
|
+
|
|
68
|
+
if (isBadge) {
|
|
69
|
+
return getBgColor(typeColor)(
|
|
70
|
+
colors.black(` ${logObj.type.toUpperCase()} `),
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const _type =
|
|
75
|
+
typeof (TYPE_ICONS as any)[logObj.type] === 'string'
|
|
76
|
+
? (TYPE_ICONS as any)[logObj.type]
|
|
77
|
+
: (logObj as any).icon || logObj.type
|
|
78
|
+
|
|
79
|
+
return _type ? getColor(typeColor)(_type) : ''
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
formatLogObj(logObj: LogObject, opts: FormatOptions) {
|
|
83
|
+
const [message, ...additional] = this.formatArgs(logObj.args, opts).split(
|
|
84
|
+
'\n',
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if (logObj.type === 'box') {
|
|
88
|
+
return box(
|
|
89
|
+
characterFormat(
|
|
90
|
+
message + (additional.length > 0 ? `\n${additional.join('\n')}` : ''),
|
|
91
|
+
),
|
|
92
|
+
{
|
|
93
|
+
title: logObj.title
|
|
94
|
+
? characterFormat(logObj.title as string)
|
|
95
|
+
: undefined,
|
|
96
|
+
style: logObj.style as BoxOpts['style'],
|
|
97
|
+
},
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const date = this.formatDate(logObj.date, opts)
|
|
102
|
+
const coloredDate = date && colors.gray(date)
|
|
103
|
+
|
|
104
|
+
const isBadge = (logObj.badge as boolean) ?? logObj.level < 2
|
|
105
|
+
const type = this.formatType(logObj, isBadge, opts)
|
|
106
|
+
|
|
107
|
+
const tag = logObj.tag ? colors.gray(logObj.tag) : ''
|
|
108
|
+
|
|
109
|
+
let line
|
|
110
|
+
const left = this.filterAndJoin([type, characterFormat(message)])
|
|
111
|
+
const right = this.filterAndJoin(opts.columns ? [tag, coloredDate] : [tag])
|
|
112
|
+
const space =
|
|
113
|
+
(opts.columns || 0) - stringWidth(left) - stringWidth(right) - 2
|
|
114
|
+
|
|
115
|
+
line =
|
|
116
|
+
space > 0 && (opts.columns || 0) >= 80
|
|
117
|
+
? left + ' '.repeat(space) + right
|
|
118
|
+
: (right ? `${colors.gray(`[${right}]`)} ` : '') + left
|
|
119
|
+
|
|
120
|
+
line += characterFormat(
|
|
121
|
+
additional.length > 0 ? `\n${additional.join('\n')}` : '',
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
if (logObj.type === 'trace') {
|
|
125
|
+
const _err = new Error(`Trace: ${logObj.message}`)
|
|
126
|
+
line += this.formatStack(_err.stack || '')
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return isBadge ? `\n${line}\n` : line
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function characterFormat(str: string) {
|
|
134
|
+
return (
|
|
135
|
+
str
|
|
136
|
+
// highlight backticks
|
|
137
|
+
.replace(/`([^`]+)`/gm, (_, m) => colors.cyan(m))
|
|
138
|
+
// underline underscores
|
|
139
|
+
.replace(/\s+_([^_]+)_\s+/gm, (_, m) => ` ${colors.underline(m)} `)
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function getColor(color = 'white') {
|
|
144
|
+
return (colors as any)[color] || colors.white
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function getBgColor(color = 'bgWhite') {
|
|
148
|
+
return (
|
|
149
|
+
(colors as any)[`bg${color[0].toUpperCase()}${color.slice(1)}`] ||
|
|
150
|
+
colors.bgWhite
|
|
151
|
+
)
|
|
152
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { createWriteStream } from 'fs'
|
|
2
|
+
import * as fs from 'fs'
|
|
3
|
+
import { dirname } from 'path'
|
|
4
|
+
import { CronJob } from 'cron'
|
|
5
|
+
import type { WriteStream } from 'fs'
|
|
6
|
+
import type { ConsolaOptions, LogObject } from '../types'
|
|
7
|
+
|
|
8
|
+
import { getLogFilePath } from '../../tool.util'
|
|
9
|
+
import { writeStream } from '../utils/stream'
|
|
10
|
+
import { LoggerReporter } from './logger'
|
|
11
|
+
|
|
12
|
+
export interface FileReporterConfig {
|
|
13
|
+
loggerDir: string
|
|
14
|
+
/**
|
|
15
|
+
* @default 'stdout_%d%.log'
|
|
16
|
+
*/
|
|
17
|
+
stdoutFileFormat?: string
|
|
18
|
+
/**
|
|
19
|
+
* @default 'error.log'
|
|
20
|
+
*/
|
|
21
|
+
stderrFileFormat?: string
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* refresh logger file stream
|
|
25
|
+
* @default '0 0 * * *'
|
|
26
|
+
*/
|
|
27
|
+
cron?: string
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Error log will be written to stdout and stderr
|
|
31
|
+
* @default false
|
|
32
|
+
*/
|
|
33
|
+
errWriteToStdout?: boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class FileReporter extends LoggerReporter {
|
|
37
|
+
constructor(private readonly configs: FileReporterConfig) {
|
|
38
|
+
super()
|
|
39
|
+
this.refreshWriteStream()
|
|
40
|
+
|
|
41
|
+
this.scheduleRefreshWriteStream()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private stdoutStream?: WriteStream
|
|
45
|
+
private stderrStream?: WriteStream
|
|
46
|
+
|
|
47
|
+
private __job?: CronJob
|
|
48
|
+
|
|
49
|
+
private scheduleRefreshWriteStream() {
|
|
50
|
+
const { cron = '0 0 * * *' } = this.configs
|
|
51
|
+
const job = new CronJob(cron, this.refreshWriteStream.bind(this))
|
|
52
|
+
job.start()
|
|
53
|
+
|
|
54
|
+
this.__job = job
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
teardown() {
|
|
58
|
+
this.__job?.stop()
|
|
59
|
+
this.stdoutStream?.end()
|
|
60
|
+
this.stderrStream?.end()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private refreshWriteStream() {
|
|
64
|
+
const {
|
|
65
|
+
loggerDir,
|
|
66
|
+
stderrFileFormat = 'error.log',
|
|
67
|
+
stdoutFileFormat = 'stdout_%d.log',
|
|
68
|
+
} = this.configs
|
|
69
|
+
|
|
70
|
+
const stdoutPath = getLogFilePath(loggerDir, stdoutFileFormat)
|
|
71
|
+
const stderrPath = getLogFilePath(loggerDir, stderrFileFormat)
|
|
72
|
+
|
|
73
|
+
createLoggerFileIfNotExist(stdoutPath)
|
|
74
|
+
createLoggerFileIfNotExist(stderrPath)
|
|
75
|
+
|
|
76
|
+
const options = {
|
|
77
|
+
encoding: 'utf-8',
|
|
78
|
+
flags: 'a+',
|
|
79
|
+
} as const
|
|
80
|
+
|
|
81
|
+
;[this.stderrStream, this.stdoutStream].forEach((stream) => {
|
|
82
|
+
stream?.end()
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
this.stdoutStream = createWriteStream(stdoutPath, options)
|
|
86
|
+
this.stderrStream = createWriteStream(stderrPath, options)
|
|
87
|
+
;[this.stderrStream, this.stdoutStream].forEach((stream) => {
|
|
88
|
+
writeStream(
|
|
89
|
+
'\n========================================================\n',
|
|
90
|
+
stream,
|
|
91
|
+
)
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
log(logObj: LogObject, ctx: { options: ConsolaOptions }) {
|
|
96
|
+
if (!this.stdoutStream || !this.stderrStream) {
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
const finalStdout = this.stdoutStream
|
|
100
|
+
const finalStderr = this.stderrStream || this.stdoutStream
|
|
101
|
+
const line = super.formatLogObj(logObj, {
|
|
102
|
+
...ctx.options.formatOptions,
|
|
103
|
+
columns: undefined,
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
if (this.configs.errWriteToStdout && logObj.level < 2) {
|
|
107
|
+
writeStream(`${line}\n`, finalStdout)
|
|
108
|
+
}
|
|
109
|
+
return writeStream(
|
|
110
|
+
`${line}\n`,
|
|
111
|
+
logObj.level < 2 ? finalStderr : finalStdout,
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const createLoggerFileIfNotExist = (path: string) => {
|
|
117
|
+
const dirPath = dirname(path)
|
|
118
|
+
|
|
119
|
+
if (!fs.existsSync(dirPath)) {
|
|
120
|
+
fs.mkdirSync(dirPath, { recursive: true })
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!fs.existsSync(path)) {
|
|
124
|
+
fs.writeFileSync(path, '', { flag: 'wx' })
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* eslint-disable prefer-rest-params */
|
|
2
|
+
import picocolors from 'picocolors'
|
|
3
|
+
import { isDevelopment } from 'std-env'
|
|
4
|
+
import type { FormatOptions, LogObject } from '../types'
|
|
5
|
+
|
|
6
|
+
import { getShortTime } from '../../tool.util'
|
|
7
|
+
import { FancyReporter } from './fancy'
|
|
8
|
+
|
|
9
|
+
export class LoggerReporter extends FancyReporter {
|
|
10
|
+
private latestLogTime: number = Date.now()
|
|
11
|
+
public formatDate(date: Date, opts: FormatOptions): string {
|
|
12
|
+
const isInVirtualTerminal = typeof opts.columns === 'undefined'
|
|
13
|
+
if (isDevelopment) {
|
|
14
|
+
const now = Date.now()
|
|
15
|
+
const delta = now - this.latestLogTime
|
|
16
|
+
this.latestLogTime = now
|
|
17
|
+
return `+${delta | 0}ms ${super.formatDate(date, opts)}`
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return isInVirtualTerminal ? '' : super.formatDate(date, opts)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public formatLogObj(logObj: LogObject, opts: FormatOptions): string {
|
|
24
|
+
const isInVirtualTerminal = typeof opts.columns === 'undefined'
|
|
25
|
+
return isInVirtualTerminal
|
|
26
|
+
? `${picocolors.gray(getShortTime(new Date()))} ${super
|
|
27
|
+
.formatLogObj(logObj, opts)
|
|
28
|
+
.replace(/^\n/, '')}`.trimEnd()
|
|
29
|
+
: super.formatLogObj(logObj, opts)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import EventEmitter from 'events'
|
|
2
|
+
import type { ConsolaInstance } from '../consola'
|
|
3
|
+
import type { ConsolaOptions, LogObject, WrappedConsola } from '../types'
|
|
4
|
+
|
|
5
|
+
import { LoggerReporter } from './logger'
|
|
6
|
+
|
|
7
|
+
export const wrapperSubscribers = (
|
|
8
|
+
consola: ConsolaInstance,
|
|
9
|
+
): WrappedConsola => {
|
|
10
|
+
Object.assign(consola, {
|
|
11
|
+
onData: (handler: (data: string) => any) =>
|
|
12
|
+
SubscriberReporter.subscriber.on('log', handler),
|
|
13
|
+
onStdOut: (handler: (data: string) => any) =>
|
|
14
|
+
SubscriberReporter.subscriber.on('stdout', handler),
|
|
15
|
+
onStdErr: (handler: (data: string) => any) =>
|
|
16
|
+
SubscriberReporter.subscriber.on('stderr', handler),
|
|
17
|
+
})
|
|
18
|
+
// @ts-expect-error
|
|
19
|
+
return consola
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class SubscriberReporter extends LoggerReporter {
|
|
23
|
+
static subscriber = new EventEmitter()
|
|
24
|
+
log(logObj: LogObject, ctx: { options: ConsolaOptions }) {
|
|
25
|
+
const line = super.formatLogObj(logObj, ctx)
|
|
26
|
+
const event = logObj.level < 2 ? 'stderr' : 'stdout'
|
|
27
|
+
SubscriberReporter.subscriber.emit(event, line)
|
|
28
|
+
SubscriberReporter.subscriber.emit('log', line)
|
|
29
|
+
}
|
|
30
|
+
}
|
package/consola/types.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ConsolaInstance } from './consola'
|
|
2
|
+
import type { LogLevel, LogType } from './constants'
|
|
3
|
+
|
|
4
|
+
export interface ConsolaOptions {
|
|
5
|
+
reporters: ConsolaReporter[]
|
|
6
|
+
types: Record<LogType, InputLogObject>
|
|
7
|
+
level: LogLevel
|
|
8
|
+
defaults: InputLogObject
|
|
9
|
+
throttle: number
|
|
10
|
+
throttleMin: number
|
|
11
|
+
stdout?: NodeJS.WriteStream
|
|
12
|
+
stderr?: NodeJS.WriteStream
|
|
13
|
+
mockFn?: (type: LogType, defaults: InputLogObject) => (...args: any) => void
|
|
14
|
+
formatOptions: FormatOptions
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @see https://nodejs.org/api/util.html#util_util_inspect_object_showhidden_depth_colors
|
|
19
|
+
*/
|
|
20
|
+
export interface FormatOptions {
|
|
21
|
+
columns?: number
|
|
22
|
+
date?: boolean
|
|
23
|
+
colors?: boolean
|
|
24
|
+
compact?: boolean | number
|
|
25
|
+
[key: string]: unknown
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface InputLogObject {
|
|
29
|
+
level?: LogLevel
|
|
30
|
+
tag?: string
|
|
31
|
+
type?: LogType
|
|
32
|
+
message?: string
|
|
33
|
+
additional?: string | string[]
|
|
34
|
+
args?: any[]
|
|
35
|
+
date?: Date
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface LogObject extends InputLogObject {
|
|
39
|
+
level: LogLevel
|
|
40
|
+
type: LogType
|
|
41
|
+
tag: string
|
|
42
|
+
args: any[]
|
|
43
|
+
date: Date
|
|
44
|
+
[key: string]: unknown
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ConsolaReporter {
|
|
48
|
+
log: (
|
|
49
|
+
logObj: LogObject,
|
|
50
|
+
ctx: {
|
|
51
|
+
options: ConsolaOptions
|
|
52
|
+
},
|
|
53
|
+
) => void
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface WrappedConsola extends ConsolaInstance {
|
|
57
|
+
onData: (handler: (data: string) => any) => WrappedConsola
|
|
58
|
+
onStdOut: (handler: (data: string) => any) => WrappedConsola
|
|
59
|
+
onStdErr: (handler: (data: string) => any) => WrappedConsola
|
|
60
|
+
}
|