@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.
@@ -0,0 +1,244 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.prettyStderrSink = exports.Printer = void 0;
7
+ const util_internal_hex_1 = require("@subsquid/util-internal-hex");
8
+ const assert_1 = __importDefault(require("assert"));
9
+ const supports_color_1 = require("supports-color");
10
+ const level_1 = require("../level");
11
+ class Printer {
12
+ constructor(out, hasColor) {
13
+ this.out = out;
14
+ this.hasColor = hasColor;
15
+ this.visited = new Set();
16
+ this.seenRecursion = false;
17
+ }
18
+ line(s) {
19
+ if (s && this.hasColor && this.style) {
20
+ s = this.style.open + s + this.style.close;
21
+ }
22
+ if (this.prefix) {
23
+ this.out(this.prefix.prepend(s));
24
+ }
25
+ else {
26
+ this.out(s);
27
+ }
28
+ }
29
+ text(text) {
30
+ for (let line of text.split(/\r?\n/)) {
31
+ this.line(line);
32
+ }
33
+ }
34
+ begin(prefix, width) {
35
+ width = width == null ? prefix.length : width;
36
+ if (this.hasColor && this.style) {
37
+ prefix = this.style.open + prefix + this.style.close;
38
+ }
39
+ this.prefix = new Prefix(prefix, width, this.prefix);
40
+ }
41
+ end() {
42
+ (0, assert_1.default)(this.prefix != null);
43
+ this.prefix = this.prefix.prev;
44
+ }
45
+ property(prefix, val) {
46
+ switch (typeof val) {
47
+ case "symbol":
48
+ case "string":
49
+ this.begin(prefix);
50
+ this.text(val.toString());
51
+ this.end();
52
+ break;
53
+ case "boolean":
54
+ case "bigint":
55
+ case "number":
56
+ this.line(`${prefix} ${val}`);
57
+ break;
58
+ case "object":
59
+ if (val instanceof Uint8Array) {
60
+ this.line(`${prefix} ${(0, util_internal_hex_1.toHex)(val)}`);
61
+ }
62
+ else if (val instanceof Date) {
63
+ this.line(`${prefix} ${val}`);
64
+ }
65
+ else if (typeof val?.toJSON == 'function') {
66
+ this.property(prefix, val.toJSON());
67
+ }
68
+ else if (Array.isArray(val)) {
69
+ if (val.length == 0) {
70
+ this.line(`${prefix} []`);
71
+ }
72
+ else {
73
+ if (this.visited.has(val)) {
74
+ this.seenRecursion = true;
75
+ return;
76
+ }
77
+ else {
78
+ this.visited.add(val);
79
+ }
80
+ this.line(prefix);
81
+ for (let item of val) {
82
+ this.property(' -', item);
83
+ }
84
+ this.visited.delete(val);
85
+ }
86
+ }
87
+ else if (val == null) {
88
+ this.line(`${prefix} null`);
89
+ }
90
+ else {
91
+ if (this.visited.has(val)) {
92
+ this.seenRecursion = true;
93
+ return;
94
+ }
95
+ else {
96
+ this.visited.add(val);
97
+ }
98
+ let has = false;
99
+ for (let key in val) {
100
+ if (!has) {
101
+ if (prefix == ' -') {
102
+ this.begin(prefix);
103
+ }
104
+ else {
105
+ this.line(prefix);
106
+ this.begin(' ');
107
+ }
108
+ }
109
+ has = true;
110
+ this.property(key + ':', val[key]);
111
+ }
112
+ if (has) {
113
+ this.end();
114
+ }
115
+ this.visited.delete(val);
116
+ }
117
+ break;
118
+ }
119
+ }
120
+ print(rec) {
121
+ this.begin(formatHead(rec, this.hasColor), 14 + (rec.ns ? rec.ns.length + 1 : 0));
122
+ if (rec.msg) {
123
+ this.text(rec.msg);
124
+ }
125
+ this.style = { open: '\u001b[2m', close: '\u001b[22m' }; // dim
126
+ if (rec.err instanceof Error) {
127
+ this.text(rec.err.stack || rec.err.toString());
128
+ }
129
+ for (let key in rec) {
130
+ switch (key) {
131
+ case 'time':
132
+ case 'ns':
133
+ case 'level':
134
+ case 'msg':
135
+ break;
136
+ default:
137
+ this.property(key + ':', rec[key]);
138
+ }
139
+ }
140
+ this.end();
141
+ if (this.seenRecursion) {
142
+ this.reset();
143
+ this.print({
144
+ ns: 'sys',
145
+ time: Date.now(),
146
+ level: level_1.LogLevel.ERROR,
147
+ msg: 'Previous record contained recursive data.\n' +
148
+ 'Serialisation of such records is not supported in production.'
149
+ });
150
+ }
151
+ }
152
+ reset() {
153
+ this.visited.clear();
154
+ this.prefix = undefined;
155
+ this.style = undefined;
156
+ this.seenRecursion = false;
157
+ }
158
+ }
159
+ exports.Printer = Printer;
160
+ function formatHead(rec, withColor) {
161
+ let time = formatTime(rec.time);
162
+ let level = level_1.LogLevel[rec.level].padEnd(5, ' ');
163
+ let ns = rec.ns;
164
+ if (withColor) {
165
+ level = `\u001b[1m\u001b[${getLevelColor(rec.level)}m${level}\u001b[0m`;
166
+ ns = `\u001b[1m\u001b[34m${ns}\u001b[0m`;
167
+ }
168
+ let head = time + ' ' + level;
169
+ if (rec.ns) {
170
+ head += ' ' + ns;
171
+ }
172
+ return head;
173
+ }
174
+ function getLevelColor(level) {
175
+ switch (level) {
176
+ case level_1.LogLevel.TRACE:
177
+ return 35;
178
+ case level_1.LogLevel.DEBUG:
179
+ return 32;
180
+ case level_1.LogLevel.INFO:
181
+ return 36;
182
+ case level_1.LogLevel.WARN:
183
+ return 33;
184
+ case level_1.LogLevel.ERROR:
185
+ case level_1.LogLevel.FATAL:
186
+ return 31;
187
+ default:
188
+ return 0;
189
+ }
190
+ }
191
+ function formatTime(time) {
192
+ let date = new Date(time);
193
+ let hour = date.getHours().toString().padStart(2, '0');
194
+ let minutes = date.getMinutes().toString().padStart(2, '0');
195
+ let seconds = date.getSeconds().toString().padStart(2, '0');
196
+ return `${hour}:${minutes}:${seconds}`;
197
+ }
198
+ class Prefix {
199
+ constructor(value, width, prev) {
200
+ this.value = value;
201
+ this.prev = prev;
202
+ this.indent = '';
203
+ this.offset = (this.prev?.offset || 0) + width + 1;
204
+ }
205
+ prepend(s) {
206
+ if (this.value) {
207
+ let val = this.value;
208
+ if (this.prev) {
209
+ val = this.prev.prepend(val);
210
+ }
211
+ this.value = '';
212
+ return s ? val + ' ' + s : val;
213
+ }
214
+ else if (s) {
215
+ this.indent = this.indent || ''.padEnd(this.offset, ' ');
216
+ return this.indent + s;
217
+ }
218
+ else {
219
+ return s;
220
+ }
221
+ }
222
+ }
223
+ const PRINTER = new Printer(line => {
224
+ process.stderr.write(line + '\n');
225
+ }, !!supports_color_1.stderr);
226
+ function prettyStderrSink(rec) {
227
+ try {
228
+ PRINTER.print(rec);
229
+ }
230
+ catch (e) {
231
+ PRINTER.reset();
232
+ PRINTER.print({
233
+ ns: 'sys',
234
+ level: level_1.LogLevel.ERROR,
235
+ time: Date.now(),
236
+ msg: e.stack || e.toString()
237
+ });
238
+ }
239
+ finally {
240
+ PRINTER.reset();
241
+ }
242
+ }
243
+ exports.prettyStderrSink = prettyStderrSink;
244
+ //# sourceMappingURL=pretty.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pretty.js","sourceRoot":"","sources":["../../src/sinks/pretty.ts"],"names":[],"mappings":";;;;;;AAAA,mEAAiD;AACjD,oDAA2B;AAC3B,mDAAoD;AACpD,oCAAiC;AAIjC,MAAa,OAAO;IAMhB,YAAoB,GAA2B,EAAU,QAAiB;QAAtD,QAAG,GAAH,GAAG,CAAwB;QAAU,aAAQ,GAAR,QAAQ,CAAS;QAJlE,YAAO,GAAG,IAAI,GAAG,EAAE,CAAA;QAEnB,kBAAa,GAAG,KAAK,CAAA;IAEgD,CAAC;IAEtE,IAAI,CAAC,CAAS;QAClB,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,EAAE;YAClC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAA;SAC7C;QACD,IAAI,IAAI,CAAC,MAAM,EAAE;YACb,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAA;SACnC;aAAM;YACH,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;SACd;IACL,CAAC;IAEO,IAAI,CAAC,IAAY;QACrB,KAAK,IAAI,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE;YAClC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;SAClB;IACL,CAAC;IAEO,KAAK,CAAC,MAAc,EAAE,KAAc;QACxC,KAAK,GAAG,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAA;QAC7C,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,EAAE;YAC7B,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAA;SACvD;QACD,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IACxD,CAAC;IAEO,GAAG;QACP,IAAA,gBAAM,EAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,CAAA;QAC3B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAA;IAClC,CAAC;IAEO,QAAQ,CAAC,MAAc,EAAE,GAAY;QACzC,QAAO,OAAO,GAAG,EAAE;YACf,KAAK,QAAQ,CAAC;YACd,KAAK,QAAQ;gBACT,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;gBAClB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAA;gBACzB,IAAI,CAAC,GAAG,EAAE,CAAA;gBACV,MAAK;YACT,KAAK,SAAS,CAAC;YACf,KAAK,QAAQ,CAAC;YACd,KAAK,QAAQ;gBACT,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC,CAAA;gBAC7B,MAAK;YACT,KAAK,QAAQ;gBACT,IAAI,GAAG,YAAY,UAAU,EAAE;oBAC3B,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,IAAA,yBAAK,EAAC,GAAG,CAAC,EAAE,CAAC,CAAA;iBACvC;qBAAM,IAAI,GAAG,YAAY,IAAI,EAAE;oBAC5B,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC,CAAA;iBAChC;qBAAM,IAAI,OAAQ,GAAW,EAAE,MAAM,IAAI,UAAU,EAAE;oBAClD,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAG,GAAW,CAAC,MAAM,EAAE,CAAC,CAAA;iBAC/C;qBAAM,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;oBAC3B,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,EAAE;wBACjB,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,KAAK,CAAC,CAAA;qBAC5B;yBAAM;wBACH,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;4BACvB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAA;4BACzB,OAAM;yBACT;6BAAM;4BACH,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;yBACxB;wBACD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;wBACjB,KAAK,IAAI,IAAI,IAAI,GAAG,EAAE;4BAClB,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;yBAC7B;wBACD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;qBAC3B;iBACJ;qBAAM,IAAI,GAAG,IAAI,IAAI,EAAE;oBACpB,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,OAAO,CAAC,CAAA;iBAC9B;qBAAM;oBACH,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;wBACvB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAA;wBACzB,OAAM;qBACT;yBAAM;wBACH,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;qBACxB;oBACD,IAAI,GAAG,GAAG,KAAK,CAAA;oBACf,KAAK,IAAI,GAAG,IAAI,GAAG,EAAE;wBACjB,IAAI,CAAC,GAAG,EAAE;4BACN,IAAI,MAAM,IAAI,KAAK,EAAE;gCACjB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;6BACrB;iCAAM;gCACH,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;gCACjB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;6BAClB;yBACJ;wBACD,GAAG,GAAG,IAAI,CAAA;wBACV,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,GAAG,EAAG,GAAW,CAAC,GAAG,CAAC,CAAC,CAAA;qBAC9C;oBACD,IAAI,GAAG,EAAE;wBACL,IAAI,CAAC,GAAG,EAAE,CAAA;qBACb;oBACD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;iBAC3B;gBACD,MAAK;SACZ;IACL,CAAC;IAED,KAAK,CAAC,GAAc;QAChB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACjF,IAAI,GAAG,CAAC,GAAG,EAAE;YACT,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;SACrB;QACD,IAAI,CAAC,KAAK,GAAG,EAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAC,CAAA,CAAC,MAAM;QAC5D,IAAI,GAAG,CAAC,GAAG,YAAY,KAAK,EAAE;YAC1B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAA;SACjD;QACD,KAAK,IAAI,GAAG,IAAI,GAAG,EAAE;YACjB,QAAO,GAAG,EAAE;gBACR,KAAK,MAAM,CAAC;gBACZ,KAAK,IAAI,CAAC;gBACV,KAAK,OAAO,CAAC;gBACb,KAAK,KAAK;oBACN,MAAK;gBACT;oBACI,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,GAAG,EAAG,GAAW,CAAC,GAAG,CAAC,CAAC,CAAA;aAClD;SACJ;QACD,IAAI,CAAC,GAAG,EAAE,CAAA;QACV,IAAI,IAAI,CAAC,aAAa,EAAE;YACpB,IAAI,CAAC,KAAK,EAAE,CAAA;YACZ,IAAI,CAAC,KAAK,CAAC;gBACP,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE;gBAChB,KAAK,EAAE,gBAAQ,CAAC,KAAK;gBACrB,GAAG,EAAE,6CAA6C;oBAC9C,+DAA+D;aACtE,CAAC,CAAA;SACL;IACL,CAAC;IAED,KAAK;QACD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;QACpB,IAAI,CAAC,MAAM,GAAG,SAAS,CAAA;QACvB,IAAI,CAAC,KAAK,GAAG,SAAS,CAAA;QACtB,IAAI,CAAC,aAAa,GAAG,KAAK,CAAA;IAC9B,CAAC;CACJ;AAhJD,0BAgJC;AAGD,SAAS,UAAU,CAAC,GAAc,EAAE,SAAmB;IACnD,IAAI,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC/B,IAAI,KAAK,GAAG,gBAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IAC9C,IAAI,EAAE,GAAG,GAAG,CAAC,EAAE,CAAA;IACf,IAAI,SAAS,EAAE;QACX,KAAK,GAAG,mBAAmB,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,CAAA;QACvE,EAAE,GAAG,sBAAsB,EAAE,WAAW,CAAA;KAC3C;IACD,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,KAAK,CAAA;IAC7B,IAAI,GAAG,CAAC,EAAE,EAAE;QACR,IAAI,IAAI,GAAG,GAAG,EAAE,CAAA;KACnB;IACD,OAAO,IAAI,CAAA;AACf,CAAC;AAGD,SAAS,aAAa,CAAC,KAAe;IAClC,QAAO,KAAK,EAAE;QACV,KAAK,gBAAQ,CAAC,KAAK;YACf,OAAO,EAAE,CAAA;QACb,KAAK,gBAAQ,CAAC,KAAK;YACf,OAAO,EAAE,CAAA;QACb,KAAK,gBAAQ,CAAC,IAAI;YACd,OAAO,EAAE,CAAA;QACb,KAAK,gBAAQ,CAAC,IAAI;YACd,OAAO,EAAE,CAAA;QACb,KAAK,gBAAQ,CAAC,KAAK,CAAC;QACpB,KAAK,gBAAQ,CAAC,KAAK;YACf,OAAO,EAAE,CAAA;QACb;YACI,OAAO,CAAC,CAAA;KACf;AACL,CAAC;AAGD,SAAS,UAAU,CAAC,IAAY;IAC5B,IAAI,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAA;IACzB,IAAI,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IACtD,IAAI,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IAC3D,IAAI,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IAC3D,OAAO,GAAG,IAAI,IAAI,OAAO,IAAI,OAAO,EAAE,CAAA;AAC1C,CAAC;AAGD,MAAM,MAAM;IAIR,YACY,KAAa,EACrB,KAAa,EACJ,IAAa;QAFd,UAAK,GAAL,KAAK,CAAQ;QAEZ,SAAI,GAAJ,IAAI,CAAS;QANlB,WAAM,GAAG,EAAE,CAAA;QAQf,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAA;IACtD,CAAC;IAED,OAAO,CAAC,CAAS;QACb,IAAI,IAAI,CAAC,KAAK,EAAE;YACZ,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,CAAA;YACpB,IAAI,IAAI,CAAC,IAAI,EAAE;gBACX,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;aAC/B;YACD,IAAI,CAAC,KAAK,GAAG,EAAE,CAAA;YACf,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;SACjC;aAAM,IAAI,CAAC,EAAE;YACV,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;YACxD,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;SACzB;aAAM;YACH,OAAO,CAAC,CAAA;SACX;IACL,CAAC;CACJ;AAGD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE;IAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAA;AACrC,CAAC,EAAE,CAAC,CAAC,uBAAW,CAAC,CAAA;AAGjB,SAAgB,gBAAgB,CAAC,GAAc;IAC3C,IAAI;QACA,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;KACrB;IAAC,OAAM,CAAM,EAAE;QACZ,OAAO,CAAC,KAAK,EAAE,CAAA;QACf,OAAO,CAAC,KAAK,CAAC;YACV,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,gBAAQ,CAAC,KAAK;YACrB,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE;YAChB,GAAG,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,QAAQ,EAAE;SAC/B,CAAC,CAAA;KACL;YAAS;QACN,OAAO,CAAC,KAAK,EAAE,CAAA;KAClB;AACL,CAAC;AAdD,4CAcC"}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@subsquid/logger",
3
+ "version": "0.0.1",
4
+ "description": "Lightweight library for structured logging",
5
+ "license": "GPL-3.0-or-later",
6
+ "repository": "git@github.com:subsquid/squid.git",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "files": [
11
+ "lib",
12
+ "src"
13
+ ],
14
+ "main": "lib/index.js",
15
+ "dependencies": {
16
+ "@subsquid/util-internal-hex": "^0.0.0",
17
+ "@subsquid/util-internal-json": "^0.1.0",
18
+ "supports-color": "^8.1.1"
19
+ },
20
+ "devDependencies": {
21
+ "@types/mocha": "^9.1.1",
22
+ "@types/node": "^16.11.22",
23
+ "@types/supports-color": "^8.1.1",
24
+ "expect": "^27.5.1",
25
+ "mocha": "^9.2.2",
26
+ "typescript": "~4.5.5"
27
+ },
28
+ "scripts": {
29
+ "build": "rm -rf lib && tsc",
30
+ "test": "mocha lib/**/*.test.js"
31
+ },
32
+ "readme": "# @subsquid/logger\n\nLightweight structured logger for squid framework.\n\n## Usage\n\n```typescript\nimport {createLogger} from \"@subsquid/logger\"\n\nconst log = createLogger('sqd:demo')\n\nlog.info('message with severity info')\nlog.debug('message with severity debug')\n\nlog.info({foo: 1, bar: 2}, 'message and some additional attributes')\n\n// info message consisting only of attributes\nlog.info({a: 1, b: 2, c: 3, array: [4, 5]}) \n\n// pass an Error object inplace of attributes\nlog.warn(new Error('Some error occured'))\n\n// Error together with some other attributes and message\nlog.error({err: new Error('Another error'), a: 1, b: 2}, 'weird')\n\n// create a child logger instance with namespace `sqd:demo:sql` \n// and `req: 1` attribute attached to every log record\nconst sqlLog = log.child('sql', {req: 1})\nsqlLog.debug('connecting to database')\nsqlLog.debug({sql: 'SELECT max(id) FROM status'})\n```\n\n## Configuration\n\nThere are 6 log levels available: `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `FATAL`.\n\nThe log level of each logger instance is solely determined by its namespace and specified\nvia set of `SQD_TRACE`, ..., `SQD_FATAL` environment variables.\nThe default log level is `INFO`. \n\nUsers override the default log level by setting in appropriate \nenvironment variable a pattern which matches the logger's namespace.\n\n* `SQD_DEBUG=*` - sets the log level to `DEBUG` for all loggers.\n* `SQD_DEBUG=foo` - loggers `foo`, `foo:bar`, `foo:a:b`, etc will have a `DEBUG` level.\n* `SQD_DEBUG=a:b*:c,d` - loggers `a:b:c`, `a:baz:c`, `a:baz:c:foo`, `d`, etc will have a `DEBUG` level, logger `a:z:c` will not.\n\nWhen logger is matched by multiple `SQD_*` variables, the match with the highest specificity wins.\nWhen specificities are equal, the most verbose matched log level will be effective.\n\nSpecificity is computed as a number of namespace characters \nwhich are not matched by wildcards and which are not part of a child namespace.\nFor example, given a pattern `foo*bar`, the specificity of `foo:bar:baz:qux`, `foobar` and `foobazbar` is `6`.\n\n## Output\n\nThe logger always writes to `stderr`. \n\nWhen `stderr` is connected to a terminal, log records will be pretty printed.\n\n![Pretty printed log records](img.png)\n\nOtherwise, log records will be written as JSON lines.\n\n```\n{\"level\":2,\"time\":1650875498437,\"ns\":\"sqd:demo\",\"msg\":\"message with severity info\"}\n{\"level\":2,\"time\":1650875498437,\"ns\":\"sqd:demo\",\"msg\":\"message and some additional attributes\",\"foo\":1,\"bar\":2}\n{\"level\":2,\"time\":1650875498437,\"ns\":\"sqd:demo\",\"a\":1,\"b\":2,\"c\":3,\"array\":[4,5]}\n{\"level\":3,\"time\":1650875498437,\"ns\":\"sqd:demo\",\"err\":{\"stack\":\"Error: Some error occured\\n at Object.<anonymous> (/Users/eldar/dev/squid/util/logger/lib/demo.js:11:10)\\n at Module._compile (node:internal/modules/cjs/loader:1103:14)\\n at Object.Module._extensions..js (node:internal/modules/cjs/loader:1155:10)\\n at Module.load (node:internal/modules/cjs/loader:981:32)\\n at Function.Module._load (node:internal/modules/cjs/loader:822:12)\\n at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)\\n at node:internal/main/run_main_module:17:47\"}}\n{\"level\":4,\"time\":1650875498438,\"ns\":\"sqd:demo\",\"msg\":\"weird\",\"err\":{\"stack\":\"Error: Another error\\n at Object.<anonymous> (/Users/eldar/dev/squid/util/logger/lib/demo.js:13:18)\\n at Module._compile (node:internal/modules/cjs/loader:1103:14)\\n at Object.Module._extensions..js (node:internal/modules/cjs/loader:1155:10)\\n at Module.load (node:internal/modules/cjs/loader:981:32)\\n at Function.Module._load (node:internal/modules/cjs/loader:822:12)\\n at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)\\n at node:internal/main/run_main_module:17:47\"},\"a\":1,\"b\":2}\n```\n"
33
+ }
package/src/demo.ts ADDED
@@ -0,0 +1,23 @@
1
+ import {createLogger} from "./index"
2
+
3
+ const log = createLogger('sqd:demo')
4
+
5
+ log.info('message with severity info')
6
+ log.debug('message with severity debug')
7
+
8
+ log.info({foo: 1, bar: 2}, 'message and some additional attributes')
9
+
10
+ // info message consisting only of attributes
11
+ log.info({a: 1, b: 2, c: 3, array: [4, 5, {a: 1, b: 2, c: {foo: 'foo', bar: 'bar'}}, {a: 3, b: 4}]})
12
+
13
+ // pass an Error object inplace of attributes
14
+ log.warn(new Error('Some error occured'))
15
+
16
+ // Error together with some other attributes and message
17
+ log.error({err: new Error('Another error'), a: 1, b: 2}, 'weird')
18
+
19
+ // create a child logger instance with namespace `sqd:demo:sql`
20
+ // and `req: 1` attribute attached to every log record
21
+ const sqlLog = log.child('sql', {req: 1})
22
+ sqlLog.debug('connecting to database')
23
+ sqlLog.debug({sql: 'SELECT max(id) FROM status'})
package/src/index.ts ADDED
@@ -0,0 +1,17 @@
1
+ import {Logger} from "./logger"
2
+ import {jsonLinesStderrSink} from "./sinks/json"
3
+ import {prettyStderrSink} from "./sinks/pretty"
4
+
5
+ export {LogLevel} from "./level"
6
+ export * from "./logger"
7
+
8
+
9
+ const ROOT = new Logger(
10
+ process.stderr.isTTY ? prettyStderrSink : jsonLinesStderrSink,
11
+ ''
12
+ )
13
+
14
+
15
+ export function createLogger(ns: string, attributes?: object): Logger {
16
+ return ROOT.child(ns, attributes)
17
+ }
@@ -0,0 +1,80 @@
1
+ import assert from "assert"
2
+ import {Levels, LogLevel} from "./level"
3
+
4
+
5
+ function suite(
6
+ config: [level: LogLevel, pattern: string][],
7
+ tests: [ns: string, level: LogLevel][]
8
+ ) {
9
+ let name = config.map(([level, pattern]) => LogLevel[level] + '=' + pattern).join('; ')
10
+ describe(name ? 'with ' + name : 'by default', function() {
11
+ let levels = new Levels()
12
+ config.forEach(([level, pattern]) => {
13
+ levels.configure(level, pattern)
14
+ })
15
+ tests.forEach(([ns, level]) => {
16
+ it(ns + '=' + LogLevel[level], () => {
17
+ assert.strictEqual(levels.get(ns), level)
18
+ })
19
+ })
20
+ })
21
+ }
22
+
23
+
24
+ describe("levels", function() {
25
+ suite([], [
26
+ ['foo', LogLevel.INFO]
27
+ ])
28
+
29
+ suite([
30
+ [LogLevel.DEBUG, '*']
31
+ ], [
32
+ ['foo', LogLevel.DEBUG],
33
+ ['bar', LogLevel.DEBUG]
34
+ ])
35
+
36
+ suite([
37
+ [LogLevel.DEBUG, 'foo']
38
+ ], [
39
+ ['foo', LogLevel.DEBUG],
40
+ ['foo:bar', LogLevel.DEBUG],
41
+ ['foobar', LogLevel.INFO]
42
+ ])
43
+
44
+ suite([
45
+ [LogLevel.DEBUG, 'foo:*']
46
+ ], [
47
+ ['foo', LogLevel.INFO],
48
+ ['foo:bar', LogLevel.DEBUG],
49
+ ['foobar', LogLevel.INFO]
50
+ ])
51
+
52
+ suite([
53
+ [LogLevel.DEBUG, 'foo*bar']
54
+ ], [
55
+ ['foobar', LogLevel.DEBUG],
56
+ ['foo:bar', LogLevel.DEBUG],
57
+ ['foo:baz:bar:qux', LogLevel.DEBUG],
58
+ ['foobaz', LogLevel.INFO]
59
+ ])
60
+
61
+ suite([
62
+ [LogLevel.DEBUG, '*'],
63
+ [LogLevel.WARN, 'foo'],
64
+ [LogLevel.ERROR, 'foo:*:bar']
65
+ ], [
66
+ ['bar', LogLevel.DEBUG],
67
+ ['foo', LogLevel.WARN],
68
+ ['foo:bar', LogLevel.WARN],
69
+ ['foo:qux:bar', LogLevel.ERROR]
70
+ ])
71
+
72
+ suite([
73
+ [LogLevel.DEBUG, 'foo, foo:*:baz'],
74
+ [LogLevel.WARN, 'foo:*']
75
+ ], [
76
+ ['foo', LogLevel.DEBUG],
77
+ ['foo:bar:baz', LogLevel.DEBUG],
78
+ ['foo:bar', LogLevel.WARN]
79
+ ])
80
+ })
package/src/level.ts ADDED
@@ -0,0 +1,96 @@
1
+ export enum LogLevel {
2
+ TRACE = 0,
3
+ DEBUG = 1,
4
+ INFO = 2,
5
+ WARN = 3,
6
+ ERROR = 4,
7
+ FATAL = 5
8
+ }
9
+
10
+
11
+ type Specificity = number
12
+
13
+
14
+ interface NamespaceMatcher {
15
+ (ns: string): Specificity
16
+ }
17
+
18
+
19
+ function compileLevelConfig(config: string): NamespaceMatcher {
20
+ let variants = config.split(',').map(ns => {
21
+ ns = ns.trim()
22
+ let regex = new RegExp('^' + ns.split('*').map(escapeRegex).join('(.*)') + '(:.*)?$')
23
+ return function match(ns: string) {
24
+ let m = regex.exec(ns)
25
+ if (!m) return 0
26
+ let specificity = ns.length + 1
27
+ for (let i = 1; i < m.length; i++) {
28
+ specificity -= m[i]?.length || 0
29
+ }
30
+ return specificity
31
+ }
32
+ })
33
+
34
+ return function matchLevel(ns: string): Specificity {
35
+ let specificity = 0
36
+ for (let i = 0; i < variants.length; i++) {
37
+ specificity = Math.max(specificity, variants[i](ns))
38
+ }
39
+ return specificity
40
+ }
41
+ }
42
+
43
+
44
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
45
+ function escapeRegex(s: string): string {
46
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
47
+ }
48
+
49
+
50
+ function noMatch(ns: string): Specificity {
51
+ return 0
52
+ }
53
+
54
+
55
+ export class Levels {
56
+ private cache: Record<string, LogLevel> = {}
57
+ private levels: NamespaceMatcher[] = [noMatch, noMatch, noMatch, noMatch, noMatch, noMatch]
58
+
59
+ get(ns: string): LogLevel {
60
+ let level = this.cache[ns]
61
+ if (level == null) {
62
+ return this.cache[ns] = this.determineLevel(ns)
63
+ } else {
64
+ return level
65
+ }
66
+ }
67
+
68
+ private determineLevel(ns: string): LogLevel {
69
+ let specificity = 0
70
+ let level = LogLevel.INFO
71
+ for (let i = 0; i < this.levels.length; i++) {
72
+ let s = this.levels[i](ns)
73
+ if (s > specificity) {
74
+ level = i
75
+ specificity = s
76
+ }
77
+ }
78
+ return level
79
+ }
80
+
81
+ configure(level: LogLevel, config: string): void {
82
+ this.levels[level] = compileLevelConfig(config)
83
+ this.cache = {}
84
+ }
85
+ }
86
+
87
+
88
+ export const LEVELS = new Levels()
89
+
90
+
91
+ ;['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'].forEach((name, level) => {
92
+ let env = process.env[`SQD_${name}`]
93
+ if (env) {
94
+ LEVELS.configure(level, env)
95
+ }
96
+ })
package/src/logger.ts ADDED
@@ -0,0 +1,146 @@
1
+ import {LEVELS, LogLevel} from "./level"
2
+
3
+
4
+ export interface LogRecord {
5
+ time: number
6
+ level: LogLevel
7
+ ns: string
8
+ msg?: string
9
+ err?: Error
10
+ }
11
+
12
+
13
+ export type Sink = (rec: LogRecord) => void
14
+
15
+
16
+ export class Logger {
17
+ constructor(
18
+ private sink: Sink,
19
+ private ns: string,
20
+ private attributes?: object
21
+ ) {
22
+ }
23
+
24
+ get level(): LogLevel {
25
+ return LEVELS.get(this.ns)
26
+ }
27
+
28
+ child(attributes: object): Logger
29
+ child(ns: string, attributes?: object): Logger
30
+ child(nsOrAttributes: string | object, attributes?: object): Logger {
31
+ let ns = this.ns
32
+ if (typeof nsOrAttributes == 'string') {
33
+ ns = ns ? `${ns}:${nsOrAttributes}` : nsOrAttributes
34
+ } else {
35
+ attributes = nsOrAttributes
36
+ }
37
+ if (this.attributes) {
38
+ if (attributes) {
39
+ attributes = {...this.attributes, ...attributes}
40
+ } else {
41
+ attributes = this.attributes
42
+ }
43
+ }
44
+ return new Logger(this.sink, ns, attributes)
45
+ }
46
+
47
+ write(level: LogLevel, msg?: string): void
48
+ write(level: LogLevel, attributes?: object, msg?: string): void
49
+ write(level: LogLevel, attributes?: any, msg?: any) {
50
+ if (attributes == null) return
51
+ if (level < this.level) return
52
+ if (typeof attributes == 'string') {
53
+ msg = attributes
54
+ attributes = null
55
+ }
56
+ if (attributes instanceof Error) {
57
+ attributes = {err: attributes}
58
+ }
59
+ let rec: any = {
60
+ level,
61
+ time: Date.now(),
62
+ ns: this.ns,
63
+ msg
64
+ }
65
+ addAttributes(this.attributes, rec)
66
+ addAttributes(attributes, rec)
67
+ this.sink(rec)
68
+ }
69
+
70
+ trace(msg?: string): void
71
+ trace(attributes?: object, msg?: string): void
72
+ trace(attributes?: any, msg?: any): void {
73
+ this.write(LogLevel.TRACE, attributes, msg)
74
+ }
75
+
76
+ debug(msg?: string): void
77
+ debug(attributes?: object, msg?: string): void
78
+ debug(attributes?: any, msg?: any): void {
79
+ this.write(LogLevel.DEBUG, attributes, msg)
80
+ }
81
+
82
+ info(msg?: string): void
83
+ info(attributes?: object, msg?: string): void
84
+ info(attributes?: any, msg?: any): void {
85
+ this.write(LogLevel.INFO, attributes, msg)
86
+ }
87
+
88
+ warn(msg?: string): void
89
+ warn(attributes?: object, msg?: string): void
90
+ warn(attributes?: any, msg?: any): void {
91
+ this.write(LogLevel.WARN, attributes, msg)
92
+ }
93
+
94
+ error(msg?: string): void
95
+ error(attributes?: object, msg?: string): void
96
+ error(attributes?: any, msg?: any): void {
97
+ this.write(LogLevel.ERROR, attributes, msg)
98
+ }
99
+
100
+ fatal(msg?: string | null): void
101
+ fatal(attributes?: object | null, msg?: string): void
102
+ fatal(attributes?: any, msg?: any): void {
103
+ this.write(LogLevel.FATAL, attributes, msg)
104
+ }
105
+
106
+ isTrace(): boolean {
107
+ return this.level <= LogLevel.TRACE
108
+ }
109
+
110
+ isDebug(): boolean {
111
+ return this.level <= LogLevel.DEBUG
112
+ }
113
+
114
+ isInfo(): boolean {
115
+ return this.level <= LogLevel.INFO
116
+ }
117
+
118
+ isWarn(): boolean {
119
+ return this.level <= LogLevel.WARN
120
+ }
121
+
122
+ isError(): boolean {
123
+ return this.level <= LogLevel.ERROR
124
+ }
125
+
126
+ isFatal(): boolean {
127
+ return this.level <= LogLevel.FATAL
128
+ }
129
+ }
130
+
131
+
132
+ function addAttributes(src: any, target: any) {
133
+ for (let key in src) {
134
+ let val = src[key]
135
+ switch (key) {
136
+ case 'time':
137
+ case 'level':
138
+ case 'ns':
139
+ case 'msg':
140
+ break
141
+ default:
142
+ target[key] = val
143
+ }
144
+ }
145
+ }
146
+
@@ -0,0 +1,23 @@
1
+ import {toJSON} from "@subsquid/util-internal-json"
2
+ import {LogLevel} from "../level"
3
+ import {LogRecord} from "../logger"
4
+
5
+
6
+ export function jsonLinesStderrSink(rec: LogRecord): void {
7
+ process.stderr.write(stringify(rec) + '\n')
8
+ }
9
+
10
+
11
+ function stringify(rec: LogRecord): string {
12
+ try {
13
+ return JSON.stringify(toJSON(rec))
14
+ } catch(e: any) {
15
+ return stringify({
16
+ ns: 'sys',
17
+ time: Date.now(),
18
+ level: LogLevel.ERROR,
19
+ msg: `Failed to serialize log record from ${rec.ns}`,
20
+ err: {stack: e.stack || e.toString()} as Error
21
+ })
22
+ }
23
+ }