infront-logger 1.0.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/README.MD ADDED
@@ -0,0 +1,43 @@
1
+ # Infront Logger
2
+ ## Logging done right
3
+
4
+ The logger supports both writhing to file and to console.
5
+
6
+ npm i @infront/logger
7
+
8
+ ## Usage
9
+ const {BaseLogger} = require('@infront/logger');
10
+ let options = {};
11
+ let logger = new BaseLogger("component1", options);
12
+ logger.log("this is a test message");
13
+
14
+ ## Loggers
15
+
16
+ ### BaseLogger
17
+ #### Methods
18
+ - session(id)
19
+ - log(msg)
20
+ - info(msg)
21
+ - error(err)
22
+ - profile(action, options)
23
+ - profileMem(options)
24
+
25
+ ### HttpLogger (extends BaseLogger)
26
+ #### Methods
27
+ - request(req)
28
+ - response(res)
29
+ - body(data)
30
+ - success(req, res, body)
31
+ - error(err)
32
+
33
+ ## options
34
+
35
+ - dirname - The directory in which the logs will be created (default: "logs")
36
+ - levels - An object of "level name : hierarchical value"
37
+ - level - default level (default: info)
38
+ - dateFormat : default "YYYY-MM-DD HH:mm:ss:ms"
39
+ - maxFileSize: The maximum file size for a rotating file strategy (default: "30m")
40
+ - maxFiles: The maximum number of files for a rotating file strategy (default: 2)
41
+ - filename - name of the default log file (default: "logs.log")
42
+ - errorFilename - name of the default error log file (default: "error.log")
43
+ - console - boolean - should log to console (default: true)
@@ -0,0 +1,355 @@
1
+ require("winston-daily-rotate-file");
2
+ const { format: format$1, createLogger, transports, addColors } = require("winston");
3
+ const { inspect } = require("util");
4
+ const colors = {
5
+ error: "red",
6
+ warn: "yellow",
7
+ info: "green",
8
+ http: "magenta",
9
+ debug: "white"
10
+ };
11
+ addColors(colors);
12
+ const OPTIONS = {
13
+ dirname: "logs",
14
+ levels: {
15
+ error: 0,
16
+ warn: 1,
17
+ info: 2,
18
+ http: 3,
19
+ debug: 4
20
+ },
21
+ level: "info",
22
+ zippedArchive: false,
23
+ dateFormat: "YYYY-MM-DD HH:mm:ss:ms",
24
+ maxFiles: 2,
25
+ maxFileSize: "30m",
26
+ filename: "logs.log",
27
+ errorFilename: "error.log",
28
+ console: true,
29
+ exclude: [],
30
+ createSymlink: true,
31
+ symlinkName: "logs.log"
32
+ };
33
+ function errorToJSON(error) {
34
+ const { message, name, stack } = error;
35
+ return {
36
+ message,
37
+ name,
38
+ stack
39
+ };
40
+ }
41
+ function fileFormatter(options) {
42
+ return format$1.combine(
43
+ // format.errors({stack: true}),
44
+ {
45
+ transform: (info) => {
46
+ const args = [info.message, ...info[Symbol.for("splat")] || []];
47
+ info.message = args.filter(Boolean).map((arg) => {
48
+ if (arg instanceof Error) {
49
+ return errorToJSON(arg);
50
+ }
51
+ return arg;
52
+ });
53
+ const msg = args.map((arg) => {
54
+ if (typeof arg == "object")
55
+ return inspect(arg, { compact: false, depth: Infinity });
56
+ return arg;
57
+ }).join(" ");
58
+ info[Symbol.for("message")] = `${info[Symbol.for("level")]}: ${msg}${info.stack ? " " + info.stack : ""}`;
59
+ if (options.exclude.some((string) => msg.includes(string)))
60
+ return null;
61
+ return info;
62
+ }
63
+ },
64
+ format$1.timestamp({ format: options.dateFormat }),
65
+ format$1.json()
66
+ );
67
+ }
68
+ function consoleFormatter(options) {
69
+ return format$1.timestamp({ format: options.dateFormat }), format$1.colorize({ all: true });
70
+ }
71
+ function getFormatter(type, options) {
72
+ switch (type) {
73
+ case "file":
74
+ return fileFormatter(options);
75
+ case "access":
76
+ break;
77
+ case "console":
78
+ return consoleFormatter(options);
79
+ }
80
+ }
81
+ function createTransport(options) {
82
+ let formatter = getFormatter(options.format, options);
83
+ if (formatter) {
84
+ options.format = formatter;
85
+ } else {
86
+ delete options.format;
87
+ }
88
+ return new transports.DailyRotateFile(options);
89
+ }
90
+ class Logger {
91
+ constructor(options = {}) {
92
+ this.options = { ...OPTIONS, ...options };
93
+ let trans = [
94
+ createTransport({
95
+ ...this.options,
96
+ filename: this.options.filename,
97
+ symlinkName: this.options.filename
98
+ }),
99
+ createTransport({
100
+ ...this.options,
101
+ filename: this.options.errorFilename,
102
+ symlinkName: this.options.errorFilename,
103
+ level: "error"
104
+ })
105
+ ];
106
+ if (this.options.console) {
107
+ trans.push(new transports.Console({
108
+ format: getFormatter("console", this.options)
109
+ }));
110
+ }
111
+ this.logger = createLogger({
112
+ level: this.options.level,
113
+ levels: this.options.levels,
114
+ exitOnError: false,
115
+ // format,
116
+ transports: trans,
117
+ exceptionHandlers: [
118
+ new transports.Console(),
119
+ new transports.File({ filename: `${this.options.dirname}/${this.options.errorFilename}` })
120
+ ]
121
+ });
122
+ }
123
+ }
124
+ const redact$1 = (data, ...sensitiveKeysList) => {
125
+ var _a, _b;
126
+ if (typeof data === "object" && data !== null && !((_b = (_a = data == null ? void 0 : data.constructor) == null ? void 0 : _a.name) == null ? void 0 : _b.startsWith("model"))) {
127
+ if (Array.isArray(data)) {
128
+ return data.map((item) => redact$1(item, ...sensitiveKeysList));
129
+ }
130
+ const redactedData = {};
131
+ for (const key in data) {
132
+ if (data == null ? void 0 : data.hasOwnProperty(key)) {
133
+ if (sensitiveKeysList.includes(key)) {
134
+ redactedData[key] = "*****";
135
+ } else {
136
+ redactedData[key] = redact$1(data[key], ...sensitiveKeysList);
137
+ }
138
+ }
139
+ }
140
+ return redactedData;
141
+ } else {
142
+ return data;
143
+ }
144
+ };
145
+ const trim$1 = (data, length) => {
146
+ try {
147
+ let str = JSON.stringify(data);
148
+ if (str.length > length) {
149
+ return str.substring(0, length) + "... [TRIMMED]";
150
+ }
151
+ return data;
152
+ } catch (err) {
153
+ return "";
154
+ }
155
+ };
156
+ const http = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
157
+ __proto__: null,
158
+ redact: redact$1,
159
+ trim: trim$1
160
+ }, Symbol.toStringTag, { value: "Module" }));
161
+ function toFixedNumber(num, digits, base) {
162
+ const pow = Math.pow(base ?? 10, digits);
163
+ return Math.round(num * pow) / pow;
164
+ }
165
+ function bytesToMB$1(b) {
166
+ return toFixedNumber(b / 1024 / 1024, 2, 10);
167
+ }
168
+ function formatBytes$1(bytes, decimals = 2) {
169
+ if (!+bytes)
170
+ return "0 Bytes";
171
+ const k = 1024;
172
+ const dm = decimals < 0 ? 0 : decimals;
173
+ const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
174
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
175
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))}${sizes[i]}`;
176
+ }
177
+ const format = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
178
+ __proto__: null,
179
+ bytesToMB: bytesToMB$1,
180
+ formatBytes: formatBytes$1,
181
+ toFixedNumber
182
+ }, Symbol.toStringTag, { value: "Module" }));
183
+ const pick = (obj, ...keys) => Object.fromEntries(
184
+ keys.filter((key) => key in obj).map((key) => [key, obj[key]])
185
+ );
186
+ const omit$1 = (obj, ...keys) => {
187
+ const result = { ...obj };
188
+ keys.forEach(function(prop) {
189
+ delete result[prop];
190
+ });
191
+ return result;
192
+ };
193
+ const object = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
194
+ __proto__: null,
195
+ omit: omit$1,
196
+ pick
197
+ }, Symbol.toStringTag, { value: "Module" }));
198
+ const v8 = require("v8");
199
+ const { bytesToMB } = format;
200
+ function stats() {
201
+ try {
202
+ let h = v8.getHeapStatistics();
203
+ return {
204
+ "total_heap_size": bytesToMB(h.total_heap_size),
205
+ "total_heap_size_executable": bytesToMB(h.total_heap_size_executable),
206
+ "total_physical_size": bytesToMB(h.total_physical_size),
207
+ "total_available_size": bytesToMB(h.total_available_size),
208
+ "used_heap_size": bytesToMB(h.used_heap_size),
209
+ "heap_size_limit": bytesToMB(h.heap_size_limit),
210
+ "malloced_memory": bytesToMB(h.malloced_memory),
211
+ "peak_malloced_memory": bytesToMB(h.peak_malloced_memory),
212
+ "does_zap_garbage": h.does_zap_garbage,
213
+ "number_of_native_contexts": h.number_of_native_contexts,
214
+ "number_of_detached_contexts": h.number_of_detached_contexts
215
+ };
216
+ } catch (err) {
217
+ return {};
218
+ }
219
+ }
220
+ class BaseLogger {
221
+ constructor(component, options) {
222
+ this.logger = new Logger(options).logger.child({ component });
223
+ this.ctx = {};
224
+ this.startTime = Date.now();
225
+ this.absaluteStartTime = Date.now();
226
+ }
227
+ session(id) {
228
+ this.ctx.sessionID = id;
229
+ return this;
230
+ }
231
+ log() {
232
+ this.logger.info(...arguments, this.ctx);
233
+ this._stopMemProfile();
234
+ }
235
+ info() {
236
+ this.logger.info(...arguments, this.ctx);
237
+ this._stopMemProfile();
238
+ }
239
+ error() {
240
+ this.logger.error(...arguments, this.ctx);
241
+ this._stopMemProfile();
242
+ }
243
+ profile(action, options = {}) {
244
+ this.ctx.profiler = this.ctx.profiler || {};
245
+ if (action) {
246
+ let startTime = this.ctx.profiler[action] ? Date.now() - this.ctx.profiler[action] : this.startTime;
247
+ this.ctx.profiler[action] = Date.now() - startTime;
248
+ }
249
+ this.ctx.profiler.totalTime = Date.now() - this.absaluteStartTime;
250
+ if (!options.continue)
251
+ this.startTime = Date.now();
252
+ return this;
253
+ }
254
+ _stopMemProfile() {
255
+ if (this.memProfileInterval)
256
+ clearInterval(this.memProfileInterval);
257
+ }
258
+ profileMem(options = {}) {
259
+ this.ctx.maxMemory = this.ctx.maxMemory || 0;
260
+ this.ctx.memoryStats = this.ctx.memoryStats || [];
261
+ this.ctx.memoryUsage = this.ctx.memoryUsage || [];
262
+ this.ctx.memoryStatsIntervalMS = options.interval || 1e3;
263
+ this._stopMemProfile();
264
+ this.memProfileInterval = setInterval(() => {
265
+ let mem = stats();
266
+ this.ctx.memoryStats.push(mem);
267
+ this.ctx.memoryUsage.push(mem.used_heap_size);
268
+ if (mem.used_heap_size > this.ctx.maxMemory) {
269
+ this.ctx.maxMemory = mem.used_heap_size;
270
+ }
271
+ }, this.ctx.memoryStatsIntervalMS);
272
+ return this;
273
+ }
274
+ context(key, value) {
275
+ this.ctx[key] = value;
276
+ return this;
277
+ }
278
+ }
279
+ const { redact, trim } = http;
280
+ const { formatBytes } = format;
281
+ const { omit } = object;
282
+ const MAX_BODY_LENGTH = 128;
283
+ class HTTPLogger extends BaseLogger {
284
+ constructor(startTime, options = {}) {
285
+ super("http", options);
286
+ this.startTime = startTime || Date.now();
287
+ }
288
+ request(req) {
289
+ this.session(req.sessionID);
290
+ this.req = req;
291
+ return this;
292
+ }
293
+ response(res) {
294
+ this.res = res;
295
+ return this;
296
+ }
297
+ body(data) {
298
+ this.data = data;
299
+ return this;
300
+ }
301
+ _prepare() {
302
+ var _a, _b;
303
+ let req = this.req;
304
+ let res = this.res;
305
+ let body = this.data;
306
+ this.ctx.request = {
307
+ headers: redact(req.headers, "cookie"),
308
+ host: req.headers.host,
309
+ baseUrl: req.baseUrl,
310
+ url: req.originalUrl || req.url,
311
+ method: req.method,
312
+ body: redact(req.body, "password"),
313
+ params: req == null ? void 0 : req.params,
314
+ query: redact(req == null ? void 0 : req.query, "password"),
315
+ clientIP: ((_a = req == null ? void 0 : req.headers["x-forwarded-for"]) == null ? void 0 : _a.split(",")[0]) ?? (req == null ? void 0 : req.socket.remoteAddress)
316
+ };
317
+ this.ctx.response = {
318
+ headers: omit(res.getHeaders(), "set-cookie", "x-powered-by"),
319
+ statusCode: res.statusCode,
320
+ body: trim(body, MAX_BODY_LENGTH)
321
+ };
322
+ this.ctx.responseTimeMs = Date.now() - this.startTime;
323
+ this.ctx.responseSizeBytes = this.data ? JSON.stringify(this.data).length : 0;
324
+ this.ctx.user = (_b = req == null ? void 0 : req.user) == null ? void 0 : _b.id;
325
+ }
326
+ _message(msg) {
327
+ var _a;
328
+ let remoteAddress = this.req.ip || this.req._remoteAddress || this.req.connection && this.req.connection.remoteAddress || void 0;
329
+ let ip = this.ctx.request.clientIP;
330
+ let method = this.ctx.request.method;
331
+ let url = this.ctx.request.url;
332
+ let statusCode = this.ctx.response.statusCode;
333
+ let responseTimeMs = this.ctx.responseTimeMs + "ms";
334
+ let responseSize = formatBytes((_a = JSON.stringify(this.data)) == null ? void 0 : _a.length);
335
+ return `${method} ${url} ${statusCode} ${responseTimeMs} ${responseSize} ${ip} ${remoteAddress} ${msg || ""}`;
336
+ }
337
+ success(req, res, body) {
338
+ if (req)
339
+ this.request(req);
340
+ if (res)
341
+ this.response(res);
342
+ if (body)
343
+ this.body(body);
344
+ this._prepare();
345
+ super.info(this._message());
346
+ }
347
+ error(err) {
348
+ this._prepare();
349
+ super.error(this._message(err));
350
+ }
351
+ }
352
+ export {
353
+ BaseLogger,
354
+ HTTPLogger as HttpLogger
355
+ };
@@ -0,0 +1,359 @@
1
+ (function(global, factory) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.index = {}));
3
+ })(this, function(exports2) {
4
+ "use strict";
5
+ require("winston-daily-rotate-file");
6
+ const { format: format$1, createLogger, transports, addColors } = require("winston");
7
+ const { inspect } = require("util");
8
+ const colors = {
9
+ error: "red",
10
+ warn: "yellow",
11
+ info: "green",
12
+ http: "magenta",
13
+ debug: "white"
14
+ };
15
+ addColors(colors);
16
+ const OPTIONS = {
17
+ dirname: "logs",
18
+ levels: {
19
+ error: 0,
20
+ warn: 1,
21
+ info: 2,
22
+ http: 3,
23
+ debug: 4
24
+ },
25
+ level: "info",
26
+ zippedArchive: false,
27
+ dateFormat: "YYYY-MM-DD HH:mm:ss:ms",
28
+ maxFiles: 2,
29
+ maxFileSize: "30m",
30
+ filename: "logs.log",
31
+ errorFilename: "error.log",
32
+ console: true,
33
+ exclude: [],
34
+ createSymlink: true,
35
+ symlinkName: "logs.log"
36
+ };
37
+ function errorToJSON(error) {
38
+ const { message, name, stack } = error;
39
+ return {
40
+ message,
41
+ name,
42
+ stack
43
+ };
44
+ }
45
+ function fileFormatter(options) {
46
+ return format$1.combine(
47
+ // format.errors({stack: true}),
48
+ {
49
+ transform: (info) => {
50
+ const args = [info.message, ...info[Symbol.for("splat")] || []];
51
+ info.message = args.filter(Boolean).map((arg) => {
52
+ if (arg instanceof Error) {
53
+ return errorToJSON(arg);
54
+ }
55
+ return arg;
56
+ });
57
+ const msg = args.map((arg) => {
58
+ if (typeof arg == "object")
59
+ return inspect(arg, { compact: false, depth: Infinity });
60
+ return arg;
61
+ }).join(" ");
62
+ info[Symbol.for("message")] = `${info[Symbol.for("level")]}: ${msg}${info.stack ? " " + info.stack : ""}`;
63
+ if (options.exclude.some((string) => msg.includes(string)))
64
+ return null;
65
+ return info;
66
+ }
67
+ },
68
+ format$1.timestamp({ format: options.dateFormat }),
69
+ format$1.json()
70
+ );
71
+ }
72
+ function consoleFormatter(options) {
73
+ return format$1.timestamp({ format: options.dateFormat }), format$1.colorize({ all: true });
74
+ }
75
+ function getFormatter(type, options) {
76
+ switch (type) {
77
+ case "file":
78
+ return fileFormatter(options);
79
+ case "access":
80
+ break;
81
+ case "console":
82
+ return consoleFormatter(options);
83
+ }
84
+ }
85
+ function createTransport(options) {
86
+ let formatter = getFormatter(options.format, options);
87
+ if (formatter) {
88
+ options.format = formatter;
89
+ } else {
90
+ delete options.format;
91
+ }
92
+ return new transports.DailyRotateFile(options);
93
+ }
94
+ class Logger {
95
+ constructor(options = {}) {
96
+ this.options = { ...OPTIONS, ...options };
97
+ let trans = [
98
+ createTransport({
99
+ ...this.options,
100
+ filename: this.options.filename,
101
+ symlinkName: this.options.filename
102
+ }),
103
+ createTransport({
104
+ ...this.options,
105
+ filename: this.options.errorFilename,
106
+ symlinkName: this.options.errorFilename,
107
+ level: "error"
108
+ })
109
+ ];
110
+ if (this.options.console) {
111
+ trans.push(new transports.Console({
112
+ format: getFormatter("console", this.options)
113
+ }));
114
+ }
115
+ this.logger = createLogger({
116
+ level: this.options.level,
117
+ levels: this.options.levels,
118
+ exitOnError: false,
119
+ // format,
120
+ transports: trans,
121
+ exceptionHandlers: [
122
+ new transports.Console(),
123
+ new transports.File({ filename: `${this.options.dirname}/${this.options.errorFilename}` })
124
+ ]
125
+ });
126
+ }
127
+ }
128
+ const redact$1 = (data, ...sensitiveKeysList) => {
129
+ var _a, _b;
130
+ if (typeof data === "object" && data !== null && !((_b = (_a = data == null ? void 0 : data.constructor) == null ? void 0 : _a.name) == null ? void 0 : _b.startsWith("model"))) {
131
+ if (Array.isArray(data)) {
132
+ return data.map((item) => redact$1(item, ...sensitiveKeysList));
133
+ }
134
+ const redactedData = {};
135
+ for (const key in data) {
136
+ if (data == null ? void 0 : data.hasOwnProperty(key)) {
137
+ if (sensitiveKeysList.includes(key)) {
138
+ redactedData[key] = "*****";
139
+ } else {
140
+ redactedData[key] = redact$1(data[key], ...sensitiveKeysList);
141
+ }
142
+ }
143
+ }
144
+ return redactedData;
145
+ } else {
146
+ return data;
147
+ }
148
+ };
149
+ const trim$1 = (data, length) => {
150
+ try {
151
+ let str = JSON.stringify(data);
152
+ if (str.length > length) {
153
+ return str.substring(0, length) + "... [TRIMMED]";
154
+ }
155
+ return data;
156
+ } catch (err) {
157
+ return "";
158
+ }
159
+ };
160
+ const http = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
161
+ __proto__: null,
162
+ redact: redact$1,
163
+ trim: trim$1
164
+ }, Symbol.toStringTag, { value: "Module" }));
165
+ function toFixedNumber(num, digits, base) {
166
+ const pow = Math.pow(base ?? 10, digits);
167
+ return Math.round(num * pow) / pow;
168
+ }
169
+ function bytesToMB$1(b) {
170
+ return toFixedNumber(b / 1024 / 1024, 2, 10);
171
+ }
172
+ function formatBytes$1(bytes, decimals = 2) {
173
+ if (!+bytes)
174
+ return "0 Bytes";
175
+ const k = 1024;
176
+ const dm = decimals < 0 ? 0 : decimals;
177
+ const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
178
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
179
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))}${sizes[i]}`;
180
+ }
181
+ const format = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
182
+ __proto__: null,
183
+ bytesToMB: bytesToMB$1,
184
+ formatBytes: formatBytes$1,
185
+ toFixedNumber
186
+ }, Symbol.toStringTag, { value: "Module" }));
187
+ const pick = (obj, ...keys) => Object.fromEntries(
188
+ keys.filter((key) => key in obj).map((key) => [key, obj[key]])
189
+ );
190
+ const omit$1 = (obj, ...keys) => {
191
+ const result = { ...obj };
192
+ keys.forEach(function(prop) {
193
+ delete result[prop];
194
+ });
195
+ return result;
196
+ };
197
+ const object = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
198
+ __proto__: null,
199
+ omit: omit$1,
200
+ pick
201
+ }, Symbol.toStringTag, { value: "Module" }));
202
+ const v8 = require("v8");
203
+ const { bytesToMB } = format;
204
+ function stats() {
205
+ try {
206
+ let h = v8.getHeapStatistics();
207
+ return {
208
+ "total_heap_size": bytesToMB(h.total_heap_size),
209
+ "total_heap_size_executable": bytesToMB(h.total_heap_size_executable),
210
+ "total_physical_size": bytesToMB(h.total_physical_size),
211
+ "total_available_size": bytesToMB(h.total_available_size),
212
+ "used_heap_size": bytesToMB(h.used_heap_size),
213
+ "heap_size_limit": bytesToMB(h.heap_size_limit),
214
+ "malloced_memory": bytesToMB(h.malloced_memory),
215
+ "peak_malloced_memory": bytesToMB(h.peak_malloced_memory),
216
+ "does_zap_garbage": h.does_zap_garbage,
217
+ "number_of_native_contexts": h.number_of_native_contexts,
218
+ "number_of_detached_contexts": h.number_of_detached_contexts
219
+ };
220
+ } catch (err) {
221
+ return {};
222
+ }
223
+ }
224
+ class BaseLogger {
225
+ constructor(component, options) {
226
+ this.logger = new Logger(options).logger.child({ component });
227
+ this.ctx = {};
228
+ this.startTime = Date.now();
229
+ this.absaluteStartTime = Date.now();
230
+ }
231
+ session(id) {
232
+ this.ctx.sessionID = id;
233
+ return this;
234
+ }
235
+ log() {
236
+ this.logger.info(...arguments, this.ctx);
237
+ this._stopMemProfile();
238
+ }
239
+ info() {
240
+ this.logger.info(...arguments, this.ctx);
241
+ this._stopMemProfile();
242
+ }
243
+ error() {
244
+ this.logger.error(...arguments, this.ctx);
245
+ this._stopMemProfile();
246
+ }
247
+ profile(action, options = {}) {
248
+ this.ctx.profiler = this.ctx.profiler || {};
249
+ if (action) {
250
+ let startTime = this.ctx.profiler[action] ? Date.now() - this.ctx.profiler[action] : this.startTime;
251
+ this.ctx.profiler[action] = Date.now() - startTime;
252
+ }
253
+ this.ctx.profiler.totalTime = Date.now() - this.absaluteStartTime;
254
+ if (!options.continue)
255
+ this.startTime = Date.now();
256
+ return this;
257
+ }
258
+ _stopMemProfile() {
259
+ if (this.memProfileInterval)
260
+ clearInterval(this.memProfileInterval);
261
+ }
262
+ profileMem(options = {}) {
263
+ this.ctx.maxMemory = this.ctx.maxMemory || 0;
264
+ this.ctx.memoryStats = this.ctx.memoryStats || [];
265
+ this.ctx.memoryUsage = this.ctx.memoryUsage || [];
266
+ this.ctx.memoryStatsIntervalMS = options.interval || 1e3;
267
+ this._stopMemProfile();
268
+ this.memProfileInterval = setInterval(() => {
269
+ let mem = stats();
270
+ this.ctx.memoryStats.push(mem);
271
+ this.ctx.memoryUsage.push(mem.used_heap_size);
272
+ if (mem.used_heap_size > this.ctx.maxMemory) {
273
+ this.ctx.maxMemory = mem.used_heap_size;
274
+ }
275
+ }, this.ctx.memoryStatsIntervalMS);
276
+ return this;
277
+ }
278
+ context(key, value) {
279
+ this.ctx[key] = value;
280
+ return this;
281
+ }
282
+ }
283
+ const { redact, trim } = http;
284
+ const { formatBytes } = format;
285
+ const { omit } = object;
286
+ const MAX_BODY_LENGTH = 128;
287
+ class HTTPLogger extends BaseLogger {
288
+ constructor(startTime, options = {}) {
289
+ super("http", options);
290
+ this.startTime = startTime || Date.now();
291
+ }
292
+ request(req) {
293
+ this.session(req.sessionID);
294
+ this.req = req;
295
+ return this;
296
+ }
297
+ response(res) {
298
+ this.res = res;
299
+ return this;
300
+ }
301
+ body(data) {
302
+ this.data = data;
303
+ return this;
304
+ }
305
+ _prepare() {
306
+ var _a, _b;
307
+ let req = this.req;
308
+ let res = this.res;
309
+ let body = this.data;
310
+ this.ctx.request = {
311
+ headers: redact(req.headers, "cookie"),
312
+ host: req.headers.host,
313
+ baseUrl: req.baseUrl,
314
+ url: req.originalUrl || req.url,
315
+ method: req.method,
316
+ body: redact(req.body, "password"),
317
+ params: req == null ? void 0 : req.params,
318
+ query: redact(req == null ? void 0 : req.query, "password"),
319
+ clientIP: ((_a = req == null ? void 0 : req.headers["x-forwarded-for"]) == null ? void 0 : _a.split(",")[0]) ?? (req == null ? void 0 : req.socket.remoteAddress)
320
+ };
321
+ this.ctx.response = {
322
+ headers: omit(res.getHeaders(), "set-cookie", "x-powered-by"),
323
+ statusCode: res.statusCode,
324
+ body: trim(body, MAX_BODY_LENGTH)
325
+ };
326
+ this.ctx.responseTimeMs = Date.now() - this.startTime;
327
+ this.ctx.responseSizeBytes = this.data ? JSON.stringify(this.data).length : 0;
328
+ this.ctx.user = (_b = req == null ? void 0 : req.user) == null ? void 0 : _b.id;
329
+ }
330
+ _message(msg) {
331
+ var _a;
332
+ let remoteAddress = this.req.ip || this.req._remoteAddress || this.req.connection && this.req.connection.remoteAddress || void 0;
333
+ let ip = this.ctx.request.clientIP;
334
+ let method = this.ctx.request.method;
335
+ let url = this.ctx.request.url;
336
+ let statusCode = this.ctx.response.statusCode;
337
+ let responseTimeMs = this.ctx.responseTimeMs + "ms";
338
+ let responseSize = formatBytes((_a = JSON.stringify(this.data)) == null ? void 0 : _a.length);
339
+ return `${method} ${url} ${statusCode} ${responseTimeMs} ${responseSize} ${ip} ${remoteAddress} ${msg || ""}`;
340
+ }
341
+ success(req, res, body) {
342
+ if (req)
343
+ this.request(req);
344
+ if (res)
345
+ this.response(res);
346
+ if (body)
347
+ this.body(body);
348
+ this._prepare();
349
+ super.info(this._message());
350
+ }
351
+ error(err) {
352
+ this._prepare();
353
+ super.error(this._message(err));
354
+ }
355
+ }
356
+ exports2.BaseLogger = BaseLogger;
357
+ exports2.HttpLogger = HTTPLogger;
358
+ Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
359
+ });
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "infront-logger",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "files": [
6
+ "dist"
7
+ ],
8
+ "main": "./dist/index.umd.js",
9
+ "module": "./dist/index.es.js",
10
+ "exports": {
11
+ ".": {
12
+ "import": "./dist/index.es.js",
13
+ "require": "./dist/index.umd.js"
14
+ }
15
+ },
16
+ "keywords": [],
17
+ "author": "",
18
+ "license": "ISC",
19
+ "devDependencies": {
20
+ "@esbuild-plugins/node-globals-polyfill": "^0.2.3",
21
+ "@esbuild-plugins/node-modules-polyfill": "^0.2.2",
22
+ "vite": "^5.0.12"
23
+ },
24
+ "dependencies": {
25
+ "winston": "^3.11.0",
26
+ "winston-daily-rotate-file": "^4.7.1",
27
+ "infront-utils": "^1.0.0"
28
+ },
29
+ "scripts": {
30
+ "test": "echo \"Error: no test specified\" && exit 1",
31
+ "dev": "vite",
32
+ "build": "vite build",
33
+ "preview": "vite preview"
34
+ }
35
+ }