@fetchmax/plugin-logger 1.0.0 → 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 +100 -28
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +100 -28
- package/package.json +6 -6
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
|
|
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
|
-
|
|
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:"
|
|
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
|
-
|
|
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
|
-
|
|
128
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
|
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:"
|
|
214
|
+
logger.log(" Data: " + dataStr.substring(0, 1e3) + "... (truncated)");
|
|
143
215
|
} else {
|
|
144
|
-
logger.log(" 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:"
|
|
246
|
+
logger.error(" Error: " + error.message);
|
|
175
247
|
if (verbose) {
|
|
176
248
|
if (error.code) {
|
|
177
|
-
logger.error(" Code:"
|
|
249
|
+
logger.error(" Code: " + error.code);
|
|
178
250
|
}
|
|
179
251
|
if (error.data) {
|
|
180
|
-
logger.error(" Data:"
|
|
252
|
+
logger.error(" Data: " + JSON.stringify(error.data));
|
|
181
253
|
}
|
|
182
254
|
if (error.stack) {
|
|
183
|
-
logger.error(" Stack:"
|
|
255
|
+
logger.error(" Stack: " + error.stack);
|
|
184
256
|
}
|
|
185
257
|
if (error.config) {
|
|
186
|
-
logger.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
|
|
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
|
-
|
|
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:"
|
|
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
|
-
|
|
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
|
-
|
|
103
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
|
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:"
|
|
189
|
+
logger.log(" Data: " + dataStr.substring(0, 1e3) + "... (truncated)");
|
|
118
190
|
} else {
|
|
119
|
-
logger.log(" 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:"
|
|
221
|
+
logger.error(" Error: " + error.message);
|
|
150
222
|
if (verbose) {
|
|
151
223
|
if (error.code) {
|
|
152
|
-
logger.error(" Code:"
|
|
224
|
+
logger.error(" Code: " + error.code);
|
|
153
225
|
}
|
|
154
226
|
if (error.data) {
|
|
155
|
-
logger.error(" Data:"
|
|
227
|
+
logger.error(" Data: " + JSON.stringify(error.data));
|
|
156
228
|
}
|
|
157
229
|
if (error.stack) {
|
|
158
|
-
logger.error(" Stack:"
|
|
230
|
+
logger.error(" Stack: " + error.stack);
|
|
159
231
|
}
|
|
160
232
|
if (error.config) {
|
|
161
|
-
logger.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.
|
|
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",
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
"require": "./dist/index.cjs"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
|
+
|
|
16
17
|
"files": [
|
|
17
|
-
"dist"
|
|
18
|
-
"README.md"
|
|
18
|
+
"dist"
|
|
19
19
|
],
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
@@ -34,13 +34,13 @@
|
|
|
34
34
|
"license": "MIT",
|
|
35
35
|
"repository": {
|
|
36
36
|
"type": "git",
|
|
37
|
-
"url": "https://github.com/
|
|
37
|
+
"url": "https://github.com/zernabhussain/fetchmax.git",
|
|
38
38
|
"directory": "packages/plugins/logger"
|
|
39
39
|
},
|
|
40
40
|
"bugs": {
|
|
41
|
-
"url": "https://github.com/
|
|
41
|
+
"url": "https://github.com/zernabhussain/fetchmax/issues"
|
|
42
42
|
},
|
|
43
|
-
"homepage": "https://github.com/
|
|
43
|
+
"homepage": "https://github.com/zernabhussain/fetchmax#readme",
|
|
44
44
|
"sideEffects": false,
|
|
45
45
|
"publishConfig": {
|
|
46
46
|
"access": "public"
|