@subsquid/logger 0.0.1
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/README.md +71 -0
- package/lib/demo.d.ts +2 -0
- package/lib/demo.d.ts.map +1 -0
- package/lib/demo.js +19 -0
- package/lib/demo.js.map +1 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +25 -0
- package/lib/index.js.map +1 -0
- package/lib/level.d.ts +17 -0
- package/lib/level.d.ts.map +1 -0
- package/lib/level.js +82 -0
- package/lib/level.js.map +1 -0
- package/lib/level.test.d.ts +2 -0
- package/lib/level.test.d.ts.map +1 -0
- package/lib/level.test.js +73 -0
- package/lib/level.test.js.map +1 -0
- package/lib/logger.d.ts +39 -0
- package/lib/logger.d.ts.map +1 -0
- package/lib/logger.js +106 -0
- package/lib/logger.js.map +1 -0
- package/lib/sinks/json.d.ts +3 -0
- package/lib/sinks/json.d.ts.map +1 -0
- package/lib/sinks/json.js +24 -0
- package/lib/sinks/json.js.map +1 -0
- package/lib/sinks/pretty.d.ts +19 -0
- package/lib/sinks/pretty.d.ts.map +1 -0
- package/lib/sinks/pretty.js +244 -0
- package/lib/sinks/pretty.js.map +1 -0
- package/package.json +33 -0
- package/src/demo.ts +23 -0
- package/src/index.ts +17 -0
- package/src/level.test.ts +80 -0
- package/src/level.ts +96 -0
- package/src/logger.ts +146 -0
- package/src/sinks/json.ts +23 -0
- package/src/sinks/pretty.ts +248 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import {toHex} from "@subsquid/util-internal-hex"
|
|
2
|
+
import assert from "assert"
|
|
3
|
+
import {stderr as stderrColor} from "supports-color"
|
|
4
|
+
import {LogLevel} from "../level"
|
|
5
|
+
import {LogRecord} from "../logger"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export class Printer {
|
|
9
|
+
private prefix?: Prefix
|
|
10
|
+
private visited = new Set()
|
|
11
|
+
private style?: {open: string, close: string}
|
|
12
|
+
private seenRecursion = false
|
|
13
|
+
|
|
14
|
+
constructor(private out: (line: string) => void, private hasColor: boolean) {}
|
|
15
|
+
|
|
16
|
+
private line(s: string): void {
|
|
17
|
+
if (s && this.hasColor && this.style) {
|
|
18
|
+
s = this.style.open + s + this.style.close
|
|
19
|
+
}
|
|
20
|
+
if (this.prefix) {
|
|
21
|
+
this.out(this.prefix.prepend(s))
|
|
22
|
+
} else {
|
|
23
|
+
this.out(s)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private text(text: string): void {
|
|
28
|
+
for (let line of text.split(/\r?\n/)) {
|
|
29
|
+
this.line(line)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private begin(prefix: string, width?: number): void {
|
|
34
|
+
width = width == null ? prefix.length : width
|
|
35
|
+
if (this.hasColor && this.style) {
|
|
36
|
+
prefix = this.style.open + prefix + this.style.close
|
|
37
|
+
}
|
|
38
|
+
this.prefix = new Prefix(prefix, width, this.prefix)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private end(): void {
|
|
42
|
+
assert(this.prefix != null)
|
|
43
|
+
this.prefix = this.prefix.prev
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private property(prefix: string, val: unknown): void {
|
|
47
|
+
switch(typeof val) {
|
|
48
|
+
case "symbol":
|
|
49
|
+
case "string":
|
|
50
|
+
this.begin(prefix)
|
|
51
|
+
this.text(val.toString())
|
|
52
|
+
this.end()
|
|
53
|
+
break
|
|
54
|
+
case "boolean":
|
|
55
|
+
case "bigint":
|
|
56
|
+
case "number":
|
|
57
|
+
this.line(`${prefix} ${val}`)
|
|
58
|
+
break
|
|
59
|
+
case "object":
|
|
60
|
+
if (val instanceof Uint8Array) {
|
|
61
|
+
this.line(`${prefix} ${toHex(val)}`)
|
|
62
|
+
} else if (val instanceof Date) {
|
|
63
|
+
this.line(`${prefix} ${val}`)
|
|
64
|
+
} else if (typeof (val as any)?.toJSON == 'function') {
|
|
65
|
+
this.property(prefix, (val as any).toJSON())
|
|
66
|
+
} else if (Array.isArray(val)) {
|
|
67
|
+
if (val.length == 0) {
|
|
68
|
+
this.line(`${prefix} []`)
|
|
69
|
+
} else {
|
|
70
|
+
if (this.visited.has(val)) {
|
|
71
|
+
this.seenRecursion = true
|
|
72
|
+
return
|
|
73
|
+
} else {
|
|
74
|
+
this.visited.add(val)
|
|
75
|
+
}
|
|
76
|
+
this.line(prefix)
|
|
77
|
+
for (let item of val) {
|
|
78
|
+
this.property(' -', item)
|
|
79
|
+
}
|
|
80
|
+
this.visited.delete(val)
|
|
81
|
+
}
|
|
82
|
+
} else if (val == null) {
|
|
83
|
+
this.line(`${prefix} null`)
|
|
84
|
+
} else {
|
|
85
|
+
if (this.visited.has(val)) {
|
|
86
|
+
this.seenRecursion = true
|
|
87
|
+
return
|
|
88
|
+
} else {
|
|
89
|
+
this.visited.add(val)
|
|
90
|
+
}
|
|
91
|
+
let has = false
|
|
92
|
+
for (let key in val) {
|
|
93
|
+
if (!has) {
|
|
94
|
+
if (prefix == ' -') {
|
|
95
|
+
this.begin(prefix)
|
|
96
|
+
} else {
|
|
97
|
+
this.line(prefix)
|
|
98
|
+
this.begin(' ')
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
has = true
|
|
102
|
+
this.property(key + ':', (val as any)[key])
|
|
103
|
+
}
|
|
104
|
+
if (has) {
|
|
105
|
+
this.end()
|
|
106
|
+
}
|
|
107
|
+
this.visited.delete(val)
|
|
108
|
+
}
|
|
109
|
+
break
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
print(rec: LogRecord) {
|
|
114
|
+
this.begin(formatHead(rec, this.hasColor), 14 + (rec.ns ? rec.ns.length + 1 : 0))
|
|
115
|
+
if (rec.msg) {
|
|
116
|
+
this.text(rec.msg)
|
|
117
|
+
}
|
|
118
|
+
this.style = {open: '\u001b[2m', close: '\u001b[22m'} // dim
|
|
119
|
+
if (rec.err instanceof Error) {
|
|
120
|
+
this.text(rec.err.stack || rec.err.toString())
|
|
121
|
+
}
|
|
122
|
+
for (let key in rec) {
|
|
123
|
+
switch(key) {
|
|
124
|
+
case 'time':
|
|
125
|
+
case 'ns':
|
|
126
|
+
case 'level':
|
|
127
|
+
case 'msg':
|
|
128
|
+
break
|
|
129
|
+
default:
|
|
130
|
+
this.property(key + ':', (rec as any)[key])
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
this.end()
|
|
134
|
+
if (this.seenRecursion) {
|
|
135
|
+
this.reset()
|
|
136
|
+
this.print({
|
|
137
|
+
ns: 'sys',
|
|
138
|
+
time: Date.now(),
|
|
139
|
+
level: LogLevel.ERROR,
|
|
140
|
+
msg: 'Previous record contained recursive data.\n' +
|
|
141
|
+
'Serialisation of such records is not supported in production.'
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
reset(): void {
|
|
147
|
+
this.visited.clear()
|
|
148
|
+
this.prefix = undefined
|
|
149
|
+
this.style = undefined
|
|
150
|
+
this.seenRecursion = false
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
function formatHead(rec: LogRecord, withColor?: boolean): string {
|
|
156
|
+
let time = formatTime(rec.time)
|
|
157
|
+
let level = LogLevel[rec.level].padEnd(5, ' ')
|
|
158
|
+
let ns = rec.ns
|
|
159
|
+
if (withColor) {
|
|
160
|
+
level = `\u001b[1m\u001b[${getLevelColor(rec.level)}m${level}\u001b[0m`
|
|
161
|
+
ns = `\u001b[1m\u001b[34m${ns}\u001b[0m`
|
|
162
|
+
}
|
|
163
|
+
let head = time + ' ' + level
|
|
164
|
+
if (rec.ns) {
|
|
165
|
+
head += ' ' + ns
|
|
166
|
+
}
|
|
167
|
+
return head
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
function getLevelColor(level: LogLevel): number {
|
|
172
|
+
switch(level) {
|
|
173
|
+
case LogLevel.TRACE:
|
|
174
|
+
return 35
|
|
175
|
+
case LogLevel.DEBUG:
|
|
176
|
+
return 32
|
|
177
|
+
case LogLevel.INFO:
|
|
178
|
+
return 36
|
|
179
|
+
case LogLevel.WARN:
|
|
180
|
+
return 33
|
|
181
|
+
case LogLevel.ERROR:
|
|
182
|
+
case LogLevel.FATAL:
|
|
183
|
+
return 31
|
|
184
|
+
default:
|
|
185
|
+
return 0
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
function formatTime(time: number): string {
|
|
191
|
+
let date = new Date(time)
|
|
192
|
+
let hour = date.getHours().toString().padStart(2, '0')
|
|
193
|
+
let minutes = date.getMinutes().toString().padStart(2, '0')
|
|
194
|
+
let seconds = date.getSeconds().toString().padStart(2, '0')
|
|
195
|
+
return `${hour}:${minutes}:${seconds}`
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class Prefix {
|
|
200
|
+
private indent = ''
|
|
201
|
+
public readonly offset: number
|
|
202
|
+
|
|
203
|
+
constructor(
|
|
204
|
+
private value: string,
|
|
205
|
+
width: number,
|
|
206
|
+
readonly prev?: Prefix
|
|
207
|
+
) {
|
|
208
|
+
this.offset = (this.prev?.offset || 0) + width + 1
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
prepend(s: string): string {
|
|
212
|
+
if (this.value) {
|
|
213
|
+
let val = this.value
|
|
214
|
+
if (this.prev) {
|
|
215
|
+
val = this.prev.prepend(val)
|
|
216
|
+
}
|
|
217
|
+
this.value = ''
|
|
218
|
+
return s ? val + ' ' + s : val
|
|
219
|
+
} else if (s) {
|
|
220
|
+
this.indent = this.indent || ''.padEnd(this.offset, ' ')
|
|
221
|
+
return this.indent + s
|
|
222
|
+
} else {
|
|
223
|
+
return s
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
const PRINTER = new Printer(line => {
|
|
230
|
+
process.stderr.write(line + '\n')
|
|
231
|
+
}, !!stderrColor)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
export function prettyStderrSink(rec: LogRecord): void {
|
|
235
|
+
try {
|
|
236
|
+
PRINTER.print(rec)
|
|
237
|
+
} catch(e: any) {
|
|
238
|
+
PRINTER.reset()
|
|
239
|
+
PRINTER.print({
|
|
240
|
+
ns: 'sys',
|
|
241
|
+
level: LogLevel.ERROR,
|
|
242
|
+
time: Date.now(),
|
|
243
|
+
msg: e.stack || e.toString()
|
|
244
|
+
})
|
|
245
|
+
} finally {
|
|
246
|
+
PRINTER.reset()
|
|
247
|
+
}
|
|
248
|
+
}
|