@cley_faye/boilerplate 3.9.1 → 3.10.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.
@@ -3,7 +3,9 @@ import { Request, Router } from "express";
3
3
  import { LogOptions, UserFromReqFunc } from "./pipelinebuilder.js";
4
4
  export declare type LoggerOptions = expressWinston.BaseLoggerOptions | boolean;
5
5
  interface ErrorLoggerConfiguration {
6
+ /** @deprecated Use collapseStacktrace */
6
7
  collapseNodeModules?: boolean;
8
+ collapseStacktrace?: boolean;
7
9
  }
8
10
  export declare type ErrorLoggerOptions = ErrorLoggerConfiguration | boolean;
9
11
  export interface LoggingData {
@@ -53,7 +53,7 @@ const customRouteFormat = (outputTimestamp) => winston_1.default.format.printf((
53
53
  }
54
54
  }
55
55
  const metaString = JSON.stringify(info.meta);
56
- const finalMessage = `${info.message} ${metaString === "{}" ? "" : metaString}`;
56
+ const finalMessage = `${info.message.toString()} ${metaString === "{}" ? "" : metaString}`;
57
57
  return (0, winston_js_1.prefixOutput)(info.level, finalMessage, outputTimestamp);
58
58
  });
59
59
  /** Return the default value for route logger is it is set as `true` */
@@ -99,16 +99,6 @@ const registerRouteLogger = (app, logOptions) => {
99
99
  }));
100
100
  };
101
101
  exports.registerRouteLogger = registerRouteLogger;
102
- const customErrorFormat = (outputTimestamp, config) => {
103
- const { collapseNodeModules } = config;
104
- return winston_1.default.format.printf((info) => {
105
- const meta = info.meta;
106
- const finalMessage = collapseNodeModules
107
- ? [...(0, winston_js_1.filterNodeModules)(meta.message)].join("\n")
108
- : meta.message;
109
- return (0, winston_js_1.prefixOutput)(info.level, finalMessage, outputTimestamp);
110
- });
111
- };
112
102
  const registerErrorLogger = (app, logOptions) => {
113
103
  const { error, logger, timestamp } = logOptions ?? {};
114
104
  const config = typeof error === "boolean"
@@ -124,7 +114,7 @@ const registerErrorLogger = (app, logOptions) => {
124
114
  else {
125
115
  app.use(express_winston_1.default.errorLogger({
126
116
  transports: winston_js_1.transports,
127
- format: winston_1.default.format.combine(winston_1.default.format.errors(), winston_1.default.format.colorize(), customErrorFormat(timestamp, config)),
117
+ format: (0, winston_js_1.customFormat)(timestamp, config.collapseStacktrace ?? config.collapseNodeModules),
128
118
  meta: false,
129
119
  }));
130
120
  }
@@ -106,6 +106,7 @@ class PipelineBuilder {
106
106
  (0, winston_js_1.setConfig)({
107
107
  timestamp: log.timestamp ?? false,
108
108
  collapseNodeModules: errorConfig.collapseNodeModules ?? true,
109
+ collapseStacktrace: errorConfig.collapseStacktrace ?? true,
109
110
  });
110
111
  }
111
112
  const router = express_1.default.Router();
package/lib/winston.d.ts CHANGED
@@ -1,17 +1,35 @@
1
1
  import winston from "winston";
2
+ import { Format } from "logform";
2
3
  /**
3
- * Collapse all lines containing /node_modules/ into an ellipsis
4
+ * Collapse useless lines in stacktrace
4
5
  */
5
- export declare const filterNodeModules: (message: string) => Generator<string>;
6
+ export declare const filterStacktrace: (message: string) => Generator<string>;
6
7
  /**
7
8
  * Prefix all lines with the level and the timestamp if needed
8
9
  */
9
10
  export declare const prefixOutput: (level: string, message: string, timestamp: boolean | undefined) => string;
10
11
  export interface LogConfig {
11
12
  timestamp: boolean;
13
+ /** @deprecated Use collapseStacktrace instead */
12
14
  collapseNodeModules: boolean;
15
+ collapseStacktrace: boolean;
13
16
  }
17
+ interface ExtendedErrorFields {
18
+ cause?: Error;
19
+ response?: {
20
+ data?: string | Record<string, unknown>;
21
+ };
22
+ request?: {
23
+ _currentUrl?: string;
24
+ };
25
+ statusCode?: number;
26
+ }
27
+ declare type ExtendedError = Error & ExtendedErrorFields;
28
+ /** Return a string with the full error and stacktrace, including causes */
29
+ export declare const filterError: (error: ExtendedError, collapseStacktrace?: boolean) => string;
30
+ export declare const customFormat: (timestamp?: boolean, collapseStacktrace?: boolean) => Format;
14
31
  /** Edit the configuration applied to consoleLogger */
15
32
  export declare const setConfig: (config: LogConfig) => void;
16
33
  export declare const transports: winston.transports.ConsoleTransportInstance[];
17
34
  export declare const consoleLogger: winston.Logger;
35
+ export {};
package/lib/winston.js CHANGED
@@ -29,30 +29,31 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
29
29
  return (mod && mod.__esModule) ? mod : { "default": mod };
30
30
  };
31
31
  Object.defineProperty(exports, "__esModule", { value: true });
32
- exports.consoleLogger = exports.transports = exports.setConfig = exports.prefixOutput = exports.filterNodeModules = void 0;
32
+ exports.consoleLogger = exports.transports = exports.setConfig = exports.customFormat = exports.filterError = exports.prefixOutput = exports.filterStacktrace = void 0;
33
33
  const winston_1 = __importDefault(require("winston"));
34
34
  // High value because of colorize()
35
- const LOG_LEVEL_PADDING_SIZE = 19;
35
+ const LOG_LEVEL_PADDING_SIZE = 10;
36
+ const stackFilterRegex = /(?:at process|at runMicrotasks)/u;
36
37
  /**
37
- * Collapse all lines containing /node_modules/ into an ellipsis
38
+ * Collapse useless lines in stacktrace
38
39
  */
39
- const filterNodeModules = function* (message) {
40
- let inNodeModules = false;
40
+ const filterStacktrace = function* (message) {
41
+ let inCollapse = false;
41
42
  const lines = message.split("\n");
42
43
  for (const line of lines) {
43
- if (/\/node_modules\//u.exec(line)) {
44
- if (!inNodeModules) {
45
- inNodeModules = true;
44
+ if (stackFilterRegex.exec(line)) {
45
+ if (!inCollapse) {
46
+ inCollapse = true;
46
47
  yield "[...]";
47
48
  }
48
49
  }
49
50
  else {
50
- inNodeModules = false;
51
+ inCollapse = false;
51
52
  yield line;
52
53
  }
53
54
  }
54
55
  };
55
- exports.filterNodeModules = filterNodeModules;
56
+ exports.filterStacktrace = filterStacktrace;
56
57
  /**
57
58
  * Prefix all lines with the level and the timestamp if needed
58
59
  */
@@ -61,21 +62,126 @@ const prefixOutput = (level, message, timestamp) => {
61
62
  ? `${new Date().toISOString()} `
62
63
  : "";
63
64
  const levelPrefix = `${level.toString().padStart(LOG_LEVEL_PADDING_SIZE)}: `;
64
- return message.split("\n").map(line => `${timestampPrefix}${levelPrefix}${line}`)
65
+ const levelEmpty = `${" ".repeat(LOG_LEVEL_PADDING_SIZE)} `;
66
+ return message.split("\n").map((line, id) => `${timestampPrefix}${id === 0 ? levelPrefix : levelEmpty}${line}`)
65
67
  .join("\n");
66
68
  };
67
69
  exports.prefixOutput = prefixOutput;
68
70
  const logConfig = {
69
71
  timestamp: true,
70
72
  collapseNodeModules: true,
73
+ collapseStacktrace: true,
71
74
  };
72
- const customFormat = winston_1.default.format.printf((info) => {
73
- const message = (("stack" in info) ? info.stack : info.message);
74
- const filteredMessage = logConfig.collapseNodeModules
75
- ? [...(0, exports.filterNodeModules)(message)].join("\n")
76
- : message;
77
- return (0, exports.prefixOutput)(info.level, filteredMessage, logConfig.timestamp);
75
+ const indent = (msg, indentLevel) => {
76
+ if (indentLevel === 0)
77
+ return msg;
78
+ const indentList = [];
79
+ for (let i = 0; i < indentLevel;) {
80
+ ++i;
81
+ const str = `${i}|`;
82
+ if (i < indentLevel) {
83
+ indentList.push(" ".repeat(str.length));
84
+ }
85
+ else {
86
+ indentList.push(str);
87
+ }
88
+ }
89
+ const indentStr = indentList.join("");
90
+ return msg.split("\n")
91
+ .map(c => `${indentStr} ${c.trimEnd()}`)
92
+ .join("\n");
93
+ };
94
+ /** Add context from axios */
95
+ const addAxiosContent = (error) => {
96
+ const res = [];
97
+ if (error.request?._currentUrl) {
98
+ res.push(`HTTP Request URL: ${error.request._currentUrl}`);
99
+ }
100
+ if (error.response?.data) {
101
+ res.push("HTTP Response body:");
102
+ if (typeof error.response.data === "string") {
103
+ res.push(error.response.data);
104
+ }
105
+ else {
106
+ try {
107
+ res.push(JSON.stringify(error.response.data));
108
+ }
109
+ catch {
110
+ try {
111
+ res.push(error.response.data.toString());
112
+ }
113
+ catch {
114
+ res.push("<cannot output body>");
115
+ }
116
+ }
117
+ }
118
+ }
119
+ if (res.length === 0)
120
+ return;
121
+ return res.join("\n");
122
+ };
123
+ /** Add context from http-errors */
124
+ const addHttpErrorContent = (error) => {
125
+ if (error.statusCode === undefined)
126
+ return;
127
+ return `HttpError status: ${error.statusCode}`;
128
+ };
129
+ /** Return a string with the full error and stacktrace, including causes */
130
+ const filterError = (error, collapseStacktrace = true) => {
131
+ const resultRows = [];
132
+ let cursor = error;
133
+ let indentLevel = 0;
134
+ while (cursor) {
135
+ if (indentLevel === 0) {
136
+ resultRows.push(cursor.name);
137
+ }
138
+ else {
139
+ resultRows.push(indent(`Caused by: ${cursor.name}`, indentLevel));
140
+ }
141
+ resultRows.push(indent(cursor.message, indentLevel));
142
+ if (cursor.stack) {
143
+ if (collapseStacktrace) {
144
+ resultRows.push(indent([...(0, exports.filterStacktrace)(cursor.stack)].join("\n"), indentLevel));
145
+ }
146
+ else {
147
+ resultRows.push(indent(cursor.stack, indentLevel));
148
+ }
149
+ }
150
+ const axiosContent = addAxiosContent(cursor);
151
+ if (axiosContent)
152
+ resultRows.push(indent(axiosContent, indentLevel));
153
+ const httpErrorsContent = addHttpErrorContent(cursor);
154
+ if (httpErrorsContent)
155
+ resultRows.push(indent(httpErrorsContent, indentLevel));
156
+ cursor = cursor.cause;
157
+ ++indentLevel;
158
+ }
159
+ return resultRows.join("\n");
160
+ };
161
+ exports.filterError = filterError;
162
+ const getErrorFromTransformable = (info) => {
163
+ if (info instanceof Error)
164
+ return info;
165
+ if (info.meta) {
166
+ const meta = info.meta;
167
+ if (meta.error && meta.error instanceof Error)
168
+ return meta.error;
169
+ }
170
+ };
171
+ const customFormat = (timestamp, collapseStacktrace) => winston_1.default.format.printf((info) => {
172
+ const effectiveTimestamp = timestamp === undefined
173
+ ? logConfig.timestamp
174
+ : timestamp;
175
+ const effectiveCollapse = collapseStacktrace === undefined
176
+ ? logConfig.collapseNodeModules || logConfig.collapseStacktrace
177
+ : collapseStacktrace;
178
+ const error = getErrorFromTransformable(info);
179
+ const message = error
180
+ ? (0, exports.filterError)(error, effectiveCollapse)
181
+ : info.message.toString();
182
+ return (0, exports.prefixOutput)(info.level, message, effectiveTimestamp);
78
183
  });
184
+ exports.customFormat = customFormat;
79
185
  /** Edit the configuration applied to consoleLogger */
80
186
  const setConfig = (config) => {
81
187
  Object.assign(logConfig, config);
@@ -84,6 +190,6 @@ exports.setConfig = setConfig;
84
190
  exports.transports = [new winston_1.default.transports.Console()];
85
191
  exports.consoleLogger = winston_1.default.createLogger({
86
192
  level: "info",
87
- format: winston_1.default.format.combine(winston_1.default.format.colorize(), customFormat),
193
+ format: (0, exports.customFormat)(),
88
194
  transports: exports.transports,
89
195
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cley_faye/boilerplate",
3
- "version": "3.9.1",
3
+ "version": "3.10.1",
4
4
  "description": "Boilerplate code generator for generic projects",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,26 +17,26 @@
17
17
  },
18
18
  "homepage": "https://github.com/CleyFaye/boilerplate#readme",
19
19
  "devDependencies": {
20
- "@keeex/eslint-config": "^1.7.1",
21
- "@types/express": "^4.17.11",
22
- "@types/grunt": "^0.4.25",
23
- "@types/http-errors": "^1.8.0",
24
- "@types/node": "^16.0.0",
25
- "@typescript-eslint/eslint-plugin": "^5.12.0",
26
- "@typescript-eslint/parser": "^5.12.0",
27
- "eslint": "^8.7.0",
28
- "eslint-plugin-import": "^2.25.4",
29
- "eslint-plugin-promise": "^6.0.0",
30
- "eslint-webpack-plugin": "^3.0.1",
31
- "express-winston": "^4.0.5",
32
- "grunt": "^1.4.1",
20
+ "@keeex/eslint-config": "^1.8.2",
21
+ "@types/express": "^4.17.14",
22
+ "@types/grunt": "^0.4.27",
23
+ "@types/http-errors": "^1.8.2",
24
+ "@types/node": "^16.11.59",
25
+ "@typescript-eslint/eslint-plugin": "^5.38.0",
26
+ "@typescript-eslint/parser": "^5.38.0",
27
+ "eslint": "^8.23.1",
28
+ "eslint-plugin-import": "^2.26.0",
29
+ "eslint-plugin-promise": "^6.0.1",
30
+ "eslint-webpack-plugin": "^3.2.0",
31
+ "express-winston": "^4.2.0",
32
+ "grunt": "^1.5.3",
33
33
  "grunt-banner": "^0.6.0",
34
- "grunt-contrib-clean": "^2.0.0",
34
+ "grunt-contrib-clean": "^2.0.1",
35
35
  "grunt-run": "^0.8.1",
36
36
  "load-grunt-tasks": "^5.1.0",
37
- "logform": "^2.3.2",
38
- "typescript": "^4.1.5",
39
- "winston": "^3.3.3"
37
+ "logform": "^2.4.2",
38
+ "typescript": "^4.8.3",
39
+ "winston": "^3.8.2"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@babel/core": "7.x",
@@ -55,9 +55,9 @@
55
55
  "grunt-contrib-watch": "1.x",
56
56
  "grunt-sass": "3.x",
57
57
  "grunt-webpack": "5.x",
58
- "resolve-typescript-plugin": "^1.1.1",
58
+ "resolve-typescript-plugin": "^1.2.0",
59
59
  "sass": "1.x",
60
- "ts-loader": "^9.2.6",
60
+ "ts-loader": "^9.4.0",
61
61
  "winston": "3.x"
62
62
  },
63
63
  "peerDependenciesMeta": {