@trojs/logger 0.5.15 → 0.5.17
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/package.json +1 -1
- package/src/logger.js +91 -15
- package/src/loggers/console.js +113 -42
package/package.json
CHANGED
package/src/logger.js
CHANGED
|
@@ -3,15 +3,9 @@ import makeLoggers from './loggers/index.js'
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @typedef {import('./models/schemas/logger.js').Logger} LoggerType
|
|
6
|
-
* @typedef {import('./models/enums/level.js').LevelType} LevelType
|
|
7
6
|
*/
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
const defaultLoggers = [
|
|
11
|
-
{
|
|
12
|
-
type: 'console'
|
|
13
|
-
}
|
|
14
|
-
]
|
|
8
|
+
const defaultLoggers = [{ type: 'console' }]
|
|
15
9
|
|
|
16
10
|
const levels = {
|
|
17
11
|
fatal: 0,
|
|
@@ -23,20 +17,102 @@ const levels = {
|
|
|
23
17
|
}
|
|
24
18
|
|
|
25
19
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* @param {
|
|
29
|
-
* @param {string
|
|
30
|
-
* @param {
|
|
31
|
-
* @
|
|
20
|
+
* Creates a Winston logger instance with custom log levels and transports.
|
|
21
|
+
* Also attaches global process event handlers for uncaught exceptions, unhandled rejections, and warnings.
|
|
22
|
+
* @param {object} [options={}] - Logger configuration options.
|
|
23
|
+
* @param {Array<{[key: string]: string}>} [options.loggers=defaultLoggers] - Array of logger transport configurations.
|
|
24
|
+
* @param {string} [options.level='info'] - Minimum log level for the logger.
|
|
25
|
+
* @param {object} [options.meta={}] - Default metadata to include in all log messages.
|
|
26
|
+
* @returns {LoggerType} Winston logger instance with custom level wrappers.
|
|
27
|
+
* These handlers will log errors and warnings using the logger, and are only attached once per process.
|
|
28
|
+
* @example
|
|
29
|
+
* import createLogger from './logger.js';
|
|
30
|
+
* const logger = createLogger({ level: 'debug', meta: { service: 'api' } });
|
|
31
|
+
* logger.info('Service started');
|
|
32
32
|
*/
|
|
33
|
-
export default ({ loggers = defaultLoggers, level = 'info', meta = {} }) => {
|
|
33
|
+
export default ({ loggers = defaultLoggers, level = 'info', meta = {} } = {}) => {
|
|
34
34
|
const winstonLoggers = makeLoggers({ winston, loggers })
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
const logger = winston.createLogger({
|
|
37
37
|
level,
|
|
38
38
|
levels,
|
|
39
39
|
defaultMeta: meta,
|
|
40
40
|
transports: winstonLoggers
|
|
41
41
|
})
|
|
42
|
+
|
|
43
|
+
const wrapLevel = (lvl) => {
|
|
44
|
+
const orig = logger[lvl].bind(logger)
|
|
45
|
+
logger[lvl] = (first, ...rest) => {
|
|
46
|
+
if (first instanceof Error) {
|
|
47
|
+
const info = {
|
|
48
|
+
level: lvl,
|
|
49
|
+
message: first.message || first.toString(),
|
|
50
|
+
error: first,
|
|
51
|
+
stack: first.stack
|
|
52
|
+
}
|
|
53
|
+
if (rest[0] && typeof rest[0] === 'object') {
|
|
54
|
+
Object.assign(info, rest[0])
|
|
55
|
+
}
|
|
56
|
+
return logger.log(info)
|
|
57
|
+
}
|
|
58
|
+
return orig(first, ...rest)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
;['fatal', 'error', 'warn', 'info', 'debug', 'trace'].forEach((lvl) => {
|
|
63
|
+
if (typeof logger[lvl] === 'function') wrapLevel(lvl)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
if (!process.__trojsLoggerHandlersAttached) {
|
|
67
|
+
process.__trojsLoggerHandlersAttached = true
|
|
68
|
+
|
|
69
|
+
process.on('uncaughtException', (err) => {
|
|
70
|
+
try {
|
|
71
|
+
logger.error(err instanceof Error ? err : new Error(String(err)))
|
|
72
|
+
} catch {
|
|
73
|
+
// eslint-disable-next-line no-console
|
|
74
|
+
console.error('UNCAUGHT_EXCEPTION', err)
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
process.on('unhandledRejection', (reason) => {
|
|
79
|
+
let err
|
|
80
|
+
if (reason instanceof Error) {
|
|
81
|
+
err = reason
|
|
82
|
+
} else if (typeof reason === 'string') {
|
|
83
|
+
err = new Error(reason)
|
|
84
|
+
} else {
|
|
85
|
+
try {
|
|
86
|
+
err = new Error(JSON.stringify(reason))
|
|
87
|
+
} catch {
|
|
88
|
+
err = new Error(String(reason))
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
logger.error(err)
|
|
93
|
+
} catch {
|
|
94
|
+
// eslint-disable-next-line no-console
|
|
95
|
+
console.error('UNHANDLED_REJECTION', err)
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
process.on('warning', (warning) => {
|
|
100
|
+
try {
|
|
101
|
+
logger.warn(
|
|
102
|
+
warning instanceof Error
|
|
103
|
+
? warning
|
|
104
|
+
: (
|
|
105
|
+
new Error(
|
|
106
|
+
`${warning.name}: ${warning.message}\n${warning.stack || ''}`
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
} catch {
|
|
111
|
+
// eslint-disable-next-line no-console
|
|
112
|
+
console.warn('PROCESS_WARNING', warning)
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return logger
|
|
42
118
|
}
|
package/src/loggers/console.js
CHANGED
|
@@ -1,59 +1,130 @@
|
|
|
1
1
|
import stackDriver from '../helpers/stackdriver.js'
|
|
2
2
|
|
|
3
|
+
const SYMBOL_MESSAGE = Symbol.for('message')
|
|
4
|
+
|
|
3
5
|
export default ({ winston, logger }) => {
|
|
4
6
|
const defaultLevel = 'trace'
|
|
7
|
+
const stackTrace = logger?.debug ?? false
|
|
5
8
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
const stackHead = (stack) =>
|
|
10
|
+
stack ? (stack.split('\n')[0] || '').trim() : ''
|
|
11
|
+
|
|
12
|
+
const ensureErrorProps = winston.format((info) => {
|
|
13
|
+
if (info instanceof Error) {
|
|
14
|
+
info.message = info.message || info.toString()
|
|
15
|
+
}
|
|
16
|
+
return info
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const extractSymbolMessage = (info) => {
|
|
20
|
+
if (
|
|
21
|
+
(!info.message || info.message === '')
|
|
22
|
+
&& info[SYMBOL_MESSAGE]
|
|
23
|
+
&& typeof info[SYMBOL_MESSAGE] === 'string'
|
|
24
|
+
) {
|
|
25
|
+
try {
|
|
26
|
+
const parsed = JSON.parse(info[SYMBOL_MESSAGE])
|
|
27
|
+
if (typeof parsed === 'string') {
|
|
28
|
+
info.message = parsed
|
|
19
29
|
}
|
|
20
|
-
}
|
|
21
|
-
info.message = info
|
|
22
|
-
} else if (typeof info.message !== 'string') {
|
|
23
|
-
info.message = JSON.stringify(info.message)
|
|
24
|
-
}
|
|
25
|
-
if (logger?.debug && info.stack) {
|
|
26
|
-
info.stacktrace = info.stack
|
|
30
|
+
} catch {
|
|
31
|
+
info.message = info[SYMBOL_MESSAGE]
|
|
27
32
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
winston.format(
|
|
31
|
-
stackDriver({ level: logger?.level, defaultLevel })
|
|
32
|
-
)(),
|
|
33
|
-
winston.format.json()
|
|
34
|
-
)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
35
|
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
const attachEmbeddedError = (info) => {
|
|
37
|
+
const embedded
|
|
38
|
+
= (info.error instanceof Error && info.error)
|
|
39
|
+
|| (info.exception instanceof Error && info.exception)
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
winston.format((info) => {
|
|
44
|
-
if (!info.message && info instanceof Error) {
|
|
41
|
+
if (info instanceof Error) {
|
|
42
|
+
if (!info.message || info.message === '') {
|
|
45
43
|
info.message = info.toString()
|
|
46
44
|
}
|
|
47
|
-
return
|
|
48
|
-
}
|
|
49
|
-
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (info.message instanceof Error) {
|
|
49
|
+
info.message = info.message.message || info.message.toString()
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (embedded) {
|
|
54
|
+
info.message = embedded.message || embedded.toString()
|
|
55
|
+
if (!info.stack && embedded.stack) {
|
|
56
|
+
info.stack = embedded.stack
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const stringifyNonStringMessage = (info) => {
|
|
62
|
+
if (info.message && typeof info.message !== 'string') {
|
|
63
|
+
try {
|
|
64
|
+
info.message = JSON.stringify(info.message)
|
|
65
|
+
} catch {
|
|
66
|
+
info.message = String(info.message)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const deriveMessageFromStack = (info) => {
|
|
72
|
+
if (info.stack && (!info.message || info.message === '')) {
|
|
73
|
+
info.message = stackHead(info.stack)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const finalizeEmptyMessage = (info) => {
|
|
78
|
+
if (!info.message || info.message === '') {
|
|
79
|
+
const clone = { ...info }
|
|
80
|
+
delete clone.level
|
|
81
|
+
delete clone.stack
|
|
82
|
+
delete clone.error
|
|
83
|
+
delete clone.exception
|
|
84
|
+
delete clone[SYMBOL_MESSAGE]
|
|
85
|
+
const keys = Object.keys(clone)
|
|
86
|
+
info.message = keys.length > 0 ? JSON.stringify(clone) : ''
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const duplicateStackTraceIfDebug = (info) => {
|
|
91
|
+
if ((logger?.debug ?? false) && info.stack) {
|
|
92
|
+
info.stacktrace = info.stack
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const normalizeMessage = winston.format((info) => {
|
|
97
|
+
extractSymbolMessage(info)
|
|
98
|
+
attachEmbeddedError(info)
|
|
99
|
+
stringifyNonStringMessage(info)
|
|
100
|
+
deriveMessageFromStack(info)
|
|
101
|
+
finalizeEmptyMessage(info)
|
|
102
|
+
duplicateStackTraceIfDebug(info)
|
|
103
|
+
return info
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
const jsonFormatter = winston.format.combine(
|
|
107
|
+
ensureErrorProps(),
|
|
108
|
+
winston.format.errors({ stack: stackTrace }),
|
|
109
|
+
normalizeMessage(),
|
|
110
|
+
winston.format(stackDriver({ level: logger?.level, defaultLevel }))(),
|
|
111
|
+
winston.format.json()
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
const simpleFormatter = winston.format.combine(
|
|
115
|
+
ensureErrorProps(),
|
|
116
|
+
winston.format.errors({ stack: stackTrace }),
|
|
117
|
+
normalizeMessage(),
|
|
118
|
+
winston.format.printf(({ level, message, stack }) => {
|
|
119
|
+
const base = `${level}: ${message || stackHead(stack)}`
|
|
120
|
+
return stack && (logger?.debug ?? false) ? `${base}\n${stack}` : base
|
|
121
|
+
})
|
|
50
122
|
)
|
|
51
123
|
|
|
52
124
|
return new winston.transports.Console({
|
|
53
125
|
level: logger?.level || defaultLevel,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
: defaultFormatter
|
|
126
|
+
handleExceptions: true,
|
|
127
|
+
handleRejections: true,
|
|
128
|
+
format: logger?.format === 'json' ? jsonFormatter : simpleFormatter
|
|
58
129
|
})
|
|
59
130
|
}
|