@fetchmax/plugin-logger 1.0.1 → 1.0.2

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/dist/index.cjs CHANGED
@@ -31,9 +31,19 @@ function loggerPlugin(config = {}) {
31
31
  logErrors = true,
32
32
  verbose = false,
33
33
  colors = true,
34
+ logHeaders = false,
35
+ logRequestBody = false,
36
+ logResponseData = false,
34
37
  filter,
35
- logger = console
38
+ logger: customLogger,
39
+ log: customLog
36
40
  } = config;
41
+ const logger = customLog ? {
42
+ log: customLog,
43
+ error: customLog,
44
+ group: void 0,
45
+ groupEnd: void 0
46
+ } : customLogger || console;
37
47
  const emoji = {
38
48
  request: "\u{1F680}",
39
49
  success: "\u2705",
@@ -56,6 +66,61 @@ function loggerPlugin(config = {}) {
56
66
  const i = Math.floor(Math.log(bytes) / Math.log(k));
57
67
  return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
58
68
  }
69
+ function redactSensitiveData(data) {
70
+ if (!data || typeof data !== "object") {
71
+ return data;
72
+ }
73
+ if (Array.isArray(data)) {
74
+ return data.map((item) => redactSensitiveData(item));
75
+ }
76
+ const redacted = {};
77
+ const sensitiveKeys = ["password", "pass", "pwd", "secret", "apikey", "api_key", "token", "accesstoken", "access_token"];
78
+ for (const [key, value] of Object.entries(data)) {
79
+ const lowerKey = key.toLowerCase();
80
+ if (sensitiveKeys.includes(lowerKey)) {
81
+ redacted[key] = "[REDACTED]";
82
+ continue;
83
+ }
84
+ if (typeof value === "string") {
85
+ const ccPattern = /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g;
86
+ if (ccPattern.test(value)) {
87
+ redacted[key] = "[REDACTED-CC]";
88
+ continue;
89
+ }
90
+ const ssnPattern = /\b\d{3}-\d{2}-\d{4}\b/g;
91
+ if (ssnPattern.test(value)) {
92
+ redacted[key] = "[REDACTED-SSN]";
93
+ continue;
94
+ }
95
+ }
96
+ if (typeof value === "object" && value !== null) {
97
+ redacted[key] = redactSensitiveData(value);
98
+ } else {
99
+ redacted[key] = value;
100
+ }
101
+ }
102
+ return redacted;
103
+ }
104
+ function redactHeaders(headers) {
105
+ if (!headers || typeof headers !== "object") {
106
+ return headers;
107
+ }
108
+ const redacted = {};
109
+ const sensitiveHeaders = ["authorization", "cookie", "x-api-key", "api-key"];
110
+ for (const [key, value] of Object.entries(headers)) {
111
+ const lowerKey = key.toLowerCase();
112
+ if (sensitiveHeaders.includes(lowerKey)) {
113
+ if (typeof value === "string" && value.length > 4) {
114
+ redacted[key] = `***${value.slice(-4)}`;
115
+ } else {
116
+ redacted[key] = "[REDACTED]";
117
+ }
118
+ } else {
119
+ redacted[key] = value;
120
+ }
121
+ }
122
+ return redacted;
123
+ }
59
124
  function getStatusColor(status) {
60
125
  if (!colors) return "";
61
126
  if (status >= 200 && status < 300) return "\x1B[32m";
@@ -81,15 +146,17 @@ function loggerPlugin(config = {}) {
81
146
  } else {
82
147
  logger.log(`${emoji.request} ${boldColor}${method}${resetColor} ${url}`);
83
148
  }
84
- if (verbose) {
85
- if (request.headers && Object.keys(request.headers).length > 0) {
86
- logger.log(" Headers:", request.headers);
149
+ if (verbose || logHeaders || logRequestBody) {
150
+ if ((verbose || logHeaders) && request.headers && Object.keys(request.headers).length > 0) {
151
+ const redactedHeaders = redactHeaders(request.headers);
152
+ logger.log(" Headers: " + JSON.stringify(redactedHeaders));
87
153
  }
88
- if (request.params && Object.keys(request.params).length > 0) {
89
- logger.log(" Params:", request.params);
154
+ if (verbose && request.params && Object.keys(request.params).length > 0) {
155
+ logger.log(" Params: " + JSON.stringify(request.params));
90
156
  }
91
- if (request.body) {
92
- logger.log(" Body:", request.body);
157
+ if ((verbose || logRequestBody) && request.body) {
158
+ const redactedBody = redactSensitiveData(request.body);
159
+ logger.log(" Body: " + JSON.stringify(redactedBody));
93
160
  }
94
161
  }
95
162
  if (logger.groupEnd) {
@@ -123,25 +190,30 @@ function loggerPlugin(config = {}) {
123
190
  `${icon} ${statusText} ${boldColor}${method}${resetColor} ${url} ${emoji.info} ${durationText}`
124
191
  );
125
192
  }
126
- if (verbose) {
127
- logger.log(" Status:", status, response.statusText);
128
- const headers = {};
129
- response.headers.forEach((value, key) => {
130
- headers[key] = value;
131
- });
132
- if (Object.keys(headers).length > 0) {
133
- logger.log(" Headers:", headers);
193
+ if (verbose || logResponseData) {
194
+ if (verbose) {
195
+ logger.log(" Status: " + status + " " + response.statusText);
134
196
  }
135
- const contentLength = response.headers.get("content-length");
136
- if (contentLength) {
137
- logger.log(" Size:", formatBytes(parseInt(contentLength, 10)));
197
+ if (verbose) {
198
+ const headers = {};
199
+ response.headers.forEach((value, key) => {
200
+ headers[key] = value;
201
+ });
202
+ if (Object.keys(headers).length > 0) {
203
+ logger.log(" Headers: " + JSON.stringify(headers));
204
+ }
205
+ const contentLength = response.headers.get("content-length");
206
+ if (contentLength) {
207
+ logger.log(" Size: " + formatBytes(parseInt(contentLength, 10)));
208
+ }
138
209
  }
139
- if (response.data) {
140
- const dataStr = JSON.stringify(response.data);
210
+ if ((verbose || logResponseData) && response.data) {
211
+ const redactedData = redactSensitiveData(response.data);
212
+ const dataStr = JSON.stringify(redactedData);
141
213
  if (dataStr.length > 1e3) {
142
- logger.log(" Data:", dataStr.substring(0, 1e3) + "... (truncated)");
214
+ logger.log(" Data: " + dataStr.substring(0, 1e3) + "... (truncated)");
143
215
  } else {
144
- logger.log(" Data:", response.data);
216
+ logger.log(" Data: " + dataStr);
145
217
  }
146
218
  }
147
219
  }
@@ -171,19 +243,19 @@ function loggerPlugin(config = {}) {
171
243
  `${emoji.error} ${statusText} ${boldColor}${method}${resetColor} ${url} ${emoji.info} ${durationText}`
172
244
  );
173
245
  }
174
- logger.error(" Error:", error.message);
246
+ logger.error(" Error: " + error.message);
175
247
  if (verbose) {
176
248
  if (error.code) {
177
- logger.error(" Code:", error.code);
249
+ logger.error(" Code: " + error.code);
178
250
  }
179
251
  if (error.data) {
180
- logger.error(" Data:", error.data);
252
+ logger.error(" Data: " + JSON.stringify(error.data));
181
253
  }
182
254
  if (error.stack) {
183
- logger.error(" Stack:", error.stack);
255
+ logger.error(" Stack: " + error.stack);
184
256
  }
185
257
  if (error.config) {
186
- logger.error(" Config:", error.config);
258
+ logger.error(" Config: " + JSON.stringify(error.config));
187
259
  }
188
260
  }
189
261
  if (logger.groupEnd) {
package/dist/index.d.cts CHANGED
@@ -11,6 +11,12 @@ interface LoggerConfig {
11
11
  verbose?: boolean;
12
12
  /** Use colors in console output (default: true) */
13
13
  colors?: boolean;
14
+ /** Log request headers (default: false) */
15
+ logHeaders?: boolean;
16
+ /** Log request body (default: false) */
17
+ logRequestBody?: boolean;
18
+ /** Log response data (default: false) */
19
+ logResponseData?: boolean;
14
20
  /** Filter function to determine which requests to log */
15
21
  filter?: (request: any) => boolean;
16
22
  /** Custom logger function */
@@ -20,6 +26,8 @@ interface LoggerConfig {
20
26
  group?: (...args: any[]) => void;
21
27
  groupEnd?: () => void;
22
28
  };
29
+ /** Custom log function (shorthand for logger.log) */
30
+ log?: (message: string) => void;
23
31
  }
24
32
  /**
25
33
  * Logger Plugin
package/dist/index.d.ts CHANGED
@@ -11,6 +11,12 @@ interface LoggerConfig {
11
11
  verbose?: boolean;
12
12
  /** Use colors in console output (default: true) */
13
13
  colors?: boolean;
14
+ /** Log request headers (default: false) */
15
+ logHeaders?: boolean;
16
+ /** Log request body (default: false) */
17
+ logRequestBody?: boolean;
18
+ /** Log response data (default: false) */
19
+ logResponseData?: boolean;
14
20
  /** Filter function to determine which requests to log */
15
21
  filter?: (request: any) => boolean;
16
22
  /** Custom logger function */
@@ -20,6 +26,8 @@ interface LoggerConfig {
20
26
  group?: (...args: any[]) => void;
21
27
  groupEnd?: () => void;
22
28
  };
29
+ /** Custom log function (shorthand for logger.log) */
30
+ log?: (message: string) => void;
23
31
  }
24
32
  /**
25
33
  * Logger Plugin
package/dist/index.js CHANGED
@@ -6,9 +6,19 @@ function loggerPlugin(config = {}) {
6
6
  logErrors = true,
7
7
  verbose = false,
8
8
  colors = true,
9
+ logHeaders = false,
10
+ logRequestBody = false,
11
+ logResponseData = false,
9
12
  filter,
10
- logger = console
13
+ logger: customLogger,
14
+ log: customLog
11
15
  } = config;
16
+ const logger = customLog ? {
17
+ log: customLog,
18
+ error: customLog,
19
+ group: void 0,
20
+ groupEnd: void 0
21
+ } : customLogger || console;
12
22
  const emoji = {
13
23
  request: "\u{1F680}",
14
24
  success: "\u2705",
@@ -31,6 +41,61 @@ function loggerPlugin(config = {}) {
31
41
  const i = Math.floor(Math.log(bytes) / Math.log(k));
32
42
  return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
33
43
  }
44
+ function redactSensitiveData(data) {
45
+ if (!data || typeof data !== "object") {
46
+ return data;
47
+ }
48
+ if (Array.isArray(data)) {
49
+ return data.map((item) => redactSensitiveData(item));
50
+ }
51
+ const redacted = {};
52
+ const sensitiveKeys = ["password", "pass", "pwd", "secret", "apikey", "api_key", "token", "accesstoken", "access_token"];
53
+ for (const [key, value] of Object.entries(data)) {
54
+ const lowerKey = key.toLowerCase();
55
+ if (sensitiveKeys.includes(lowerKey)) {
56
+ redacted[key] = "[REDACTED]";
57
+ continue;
58
+ }
59
+ if (typeof value === "string") {
60
+ const ccPattern = /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g;
61
+ if (ccPattern.test(value)) {
62
+ redacted[key] = "[REDACTED-CC]";
63
+ continue;
64
+ }
65
+ const ssnPattern = /\b\d{3}-\d{2}-\d{4}\b/g;
66
+ if (ssnPattern.test(value)) {
67
+ redacted[key] = "[REDACTED-SSN]";
68
+ continue;
69
+ }
70
+ }
71
+ if (typeof value === "object" && value !== null) {
72
+ redacted[key] = redactSensitiveData(value);
73
+ } else {
74
+ redacted[key] = value;
75
+ }
76
+ }
77
+ return redacted;
78
+ }
79
+ function redactHeaders(headers) {
80
+ if (!headers || typeof headers !== "object") {
81
+ return headers;
82
+ }
83
+ const redacted = {};
84
+ const sensitiveHeaders = ["authorization", "cookie", "x-api-key", "api-key"];
85
+ for (const [key, value] of Object.entries(headers)) {
86
+ const lowerKey = key.toLowerCase();
87
+ if (sensitiveHeaders.includes(lowerKey)) {
88
+ if (typeof value === "string" && value.length > 4) {
89
+ redacted[key] = `***${value.slice(-4)}`;
90
+ } else {
91
+ redacted[key] = "[REDACTED]";
92
+ }
93
+ } else {
94
+ redacted[key] = value;
95
+ }
96
+ }
97
+ return redacted;
98
+ }
34
99
  function getStatusColor(status) {
35
100
  if (!colors) return "";
36
101
  if (status >= 200 && status < 300) return "\x1B[32m";
@@ -56,15 +121,17 @@ function loggerPlugin(config = {}) {
56
121
  } else {
57
122
  logger.log(`${emoji.request} ${boldColor}${method}${resetColor} ${url}`);
58
123
  }
59
- if (verbose) {
60
- if (request.headers && Object.keys(request.headers).length > 0) {
61
- logger.log(" Headers:", request.headers);
124
+ if (verbose || logHeaders || logRequestBody) {
125
+ if ((verbose || logHeaders) && request.headers && Object.keys(request.headers).length > 0) {
126
+ const redactedHeaders = redactHeaders(request.headers);
127
+ logger.log(" Headers: " + JSON.stringify(redactedHeaders));
62
128
  }
63
- if (request.params && Object.keys(request.params).length > 0) {
64
- logger.log(" Params:", request.params);
129
+ if (verbose && request.params && Object.keys(request.params).length > 0) {
130
+ logger.log(" Params: " + JSON.stringify(request.params));
65
131
  }
66
- if (request.body) {
67
- logger.log(" Body:", request.body);
132
+ if ((verbose || logRequestBody) && request.body) {
133
+ const redactedBody = redactSensitiveData(request.body);
134
+ logger.log(" Body: " + JSON.stringify(redactedBody));
68
135
  }
69
136
  }
70
137
  if (logger.groupEnd) {
@@ -98,25 +165,30 @@ function loggerPlugin(config = {}) {
98
165
  `${icon} ${statusText} ${boldColor}${method}${resetColor} ${url} ${emoji.info} ${durationText}`
99
166
  );
100
167
  }
101
- if (verbose) {
102
- logger.log(" Status:", status, response.statusText);
103
- const headers = {};
104
- response.headers.forEach((value, key) => {
105
- headers[key] = value;
106
- });
107
- if (Object.keys(headers).length > 0) {
108
- logger.log(" Headers:", headers);
168
+ if (verbose || logResponseData) {
169
+ if (verbose) {
170
+ logger.log(" Status: " + status + " " + response.statusText);
109
171
  }
110
- const contentLength = response.headers.get("content-length");
111
- if (contentLength) {
112
- logger.log(" Size:", formatBytes(parseInt(contentLength, 10)));
172
+ if (verbose) {
173
+ const headers = {};
174
+ response.headers.forEach((value, key) => {
175
+ headers[key] = value;
176
+ });
177
+ if (Object.keys(headers).length > 0) {
178
+ logger.log(" Headers: " + JSON.stringify(headers));
179
+ }
180
+ const contentLength = response.headers.get("content-length");
181
+ if (contentLength) {
182
+ logger.log(" Size: " + formatBytes(parseInt(contentLength, 10)));
183
+ }
113
184
  }
114
- if (response.data) {
115
- const dataStr = JSON.stringify(response.data);
185
+ if ((verbose || logResponseData) && response.data) {
186
+ const redactedData = redactSensitiveData(response.data);
187
+ const dataStr = JSON.stringify(redactedData);
116
188
  if (dataStr.length > 1e3) {
117
- logger.log(" Data:", dataStr.substring(0, 1e3) + "... (truncated)");
189
+ logger.log(" Data: " + dataStr.substring(0, 1e3) + "... (truncated)");
118
190
  } else {
119
- logger.log(" Data:", response.data);
191
+ logger.log(" Data: " + dataStr);
120
192
  }
121
193
  }
122
194
  }
@@ -146,19 +218,19 @@ function loggerPlugin(config = {}) {
146
218
  `${emoji.error} ${statusText} ${boldColor}${method}${resetColor} ${url} ${emoji.info} ${durationText}`
147
219
  );
148
220
  }
149
- logger.error(" Error:", error.message);
221
+ logger.error(" Error: " + error.message);
150
222
  if (verbose) {
151
223
  if (error.code) {
152
- logger.error(" Code:", error.code);
224
+ logger.error(" Code: " + error.code);
153
225
  }
154
226
  if (error.data) {
155
- logger.error(" Data:", error.data);
227
+ logger.error(" Data: " + JSON.stringify(error.data));
156
228
  }
157
229
  if (error.stack) {
158
- logger.error(" Stack:", error.stack);
230
+ logger.error(" Stack: " + error.stack);
159
231
  }
160
232
  if (error.config) {
161
- logger.error(" Config:", error.config);
233
+ logger.error(" Config: " + JSON.stringify(error.config));
162
234
  }
163
235
  }
164
236
  if (logger.groupEnd) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fetchmax/plugin-logger",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Logger plugin for FetchMax with detailed request/response logging",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",