sasai-common-utils 1.0.51 → 1.0.53
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/package.json
CHANGED
|
@@ -6,11 +6,20 @@ const { LOG_LEVELS } = require("../logger/constants");
|
|
|
6
6
|
* @param {Object} logger - Logger instance.
|
|
7
7
|
*/
|
|
8
8
|
const logRequest = (request, logger) => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
const { method, url, headers, data, params } = request;
|
|
10
|
+
const api = "REQUEST";
|
|
11
|
+
|
|
12
|
+
const logObject = {
|
|
13
|
+
method,
|
|
14
|
+
url,
|
|
15
|
+
headers,
|
|
16
|
+
requestBody: data,
|
|
17
|
+
parameters: params,
|
|
18
|
+
api,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
logger.logInfo("Axios Request", logObject);
|
|
22
|
+
return request;
|
|
14
23
|
};
|
|
15
24
|
|
|
16
25
|
/**
|
|
@@ -19,12 +28,24 @@ const logRequest = (request, logger) => {
|
|
|
19
28
|
* @param {Object} logger - Logger instance.
|
|
20
29
|
*/
|
|
21
30
|
const logResponse = (response, logger) => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
const { status, config, data, headers: responseHeaders } = response;
|
|
32
|
+
const { method, url, headers, data: requestBody, params } = config || {};
|
|
33
|
+
const api = "RESPONSE";
|
|
34
|
+
|
|
35
|
+
const logObject = {
|
|
36
|
+
statusCode: status,
|
|
37
|
+
method,
|
|
38
|
+
url,
|
|
39
|
+
headers, // request headers
|
|
40
|
+
responseHeaders, // actual response headers
|
|
41
|
+
requestBody,
|
|
42
|
+
responseBody: data,
|
|
43
|
+
parameters: params,
|
|
44
|
+
api,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
logger.logInfo("Axios Response", logObject);
|
|
48
|
+
return response;
|
|
28
49
|
};
|
|
29
50
|
|
|
30
51
|
/**
|
|
@@ -33,53 +54,55 @@ const logResponse = (response, logger) => {
|
|
|
33
54
|
* @param {Object} logger - Logger instance.
|
|
34
55
|
*/
|
|
35
56
|
const logError = (error, logger) => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
57
|
+
const { response, config } = error || {};
|
|
58
|
+
const { method, url, headers, data: requestBody, params } = config || {};
|
|
59
|
+
|
|
60
|
+
const logObject = {
|
|
61
|
+
step: "Axios Error",
|
|
62
|
+
error,
|
|
63
|
+
method,
|
|
64
|
+
url,
|
|
65
|
+
headers,
|
|
66
|
+
requestBody,
|
|
67
|
+
parameters: params,
|
|
68
|
+
responseBody: response?.data || {},
|
|
69
|
+
statusCode: response?.status || "",
|
|
70
|
+
responseHeaders: response?.headers || {},
|
|
71
|
+
api: "ERROR",
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
logger.logError(logObject);
|
|
75
|
+
return Promise.reject(error);
|
|
47
76
|
};
|
|
48
77
|
|
|
49
78
|
/**
|
|
50
79
|
* Attaches logging interceptors to an Axios instance.
|
|
51
80
|
* @param {Object} axiosInstance - Axios instance.
|
|
52
81
|
* @param {Object} logger - Logger instance.
|
|
82
|
+
* @param {Object} traceContext - Trace context provider.
|
|
53
83
|
*/
|
|
54
84
|
const attachAxiosLogger = (axiosInstance, logger, traceContext) => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
(error) => logError(error, logger)
|
|
67
|
-
);
|
|
85
|
+
axiosInstance.interceptors.request.use(
|
|
86
|
+
(request) => {
|
|
87
|
+
const currentTraceContext =
|
|
88
|
+
traceContext?.getCurrentTraceContext?.() || {};
|
|
89
|
+
|
|
90
|
+
request.headers = {
|
|
91
|
+
...request.headers,
|
|
92
|
+
"x-b3-traceid": currentTraceContext?.trace_id ?? "",
|
|
93
|
+
"x-b3-spanid": currentTraceContext?.span_id ?? "",
|
|
94
|
+
"x-b3-parentspanid": currentTraceContext?.parent_span_id ?? "",
|
|
95
|
+
};
|
|
68
96
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
...response.headers,
|
|
74
|
-
"x-b3-traceid": currentTraceContext?.trace_id ?? "",
|
|
75
|
-
"x-b3-spanid": currentTraceContext?.span_id ?? "",
|
|
76
|
-
"x-b3-parentspanid": currentTraceContext?.parent_span_id ?? ""
|
|
97
|
+
return logRequest(request, logger);
|
|
98
|
+
},
|
|
99
|
+
(error) => logError(error, logger),
|
|
100
|
+
);
|
|
77
101
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
);
|
|
102
|
+
axiosInstance.interceptors.response.use(
|
|
103
|
+
(response) => logResponse(response, logger),
|
|
104
|
+
(error) => logError(error, logger),
|
|
105
|
+
);
|
|
83
106
|
};
|
|
84
107
|
|
|
85
108
|
module.exports = { attachAxiosLogger };
|
|
@@ -41,6 +41,11 @@ function safeJsonStringify(value) {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
function normalizePayload(value) {
|
|
45
|
+
if (typeof value === "string") return cleanString(value);
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
|
|
44
49
|
function decodeTokenDetails(headers = {}) {
|
|
45
50
|
try {
|
|
46
51
|
const authHeader = headers?.authorization || headers?.Authorization;
|
|
@@ -155,11 +160,17 @@ function emitLog(level, data) {
|
|
|
155
160
|
|
|
156
161
|
const { trace, span, parentSpan, message, ...rest } = data;
|
|
157
162
|
|
|
163
|
+
const finalMessage =
|
|
164
|
+
typeof message === "string" && message.trim()
|
|
165
|
+
? cleanString(message)
|
|
166
|
+
: "Application log";
|
|
167
|
+
|
|
158
168
|
pinoLogger[level]({
|
|
159
169
|
trace_id: trace || undefined,
|
|
160
170
|
span_id: span || undefined,
|
|
161
171
|
parent_span_id: parentSpan || undefined,
|
|
162
|
-
|
|
172
|
+
body: finalMessage,
|
|
173
|
+
message: finalMessage,
|
|
163
174
|
...rest,
|
|
164
175
|
});
|
|
165
176
|
}
|
|
@@ -181,20 +192,18 @@ function createLogger(config) {
|
|
|
181
192
|
parentSpan: traceContext.parent_span_id,
|
|
182
193
|
thread: "",
|
|
183
194
|
class: "",
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
responseBody: data?.responseBody || {},
|
|
197
|
-
}),
|
|
195
|
+
message,
|
|
196
|
+
step: data?.step || "",
|
|
197
|
+
method: data?.method?.toUpperCase() || "",
|
|
198
|
+
parameters: normalizePayload(data?.parameters || ""),
|
|
199
|
+
path: data?.url || "",
|
|
200
|
+
headers: normalizePayload(data?.headers || {}),
|
|
201
|
+
responseHeaders: normalizePayload(data?.responseHeaders || {}),
|
|
202
|
+
api: data?.api || "",
|
|
203
|
+
tokenDetails,
|
|
204
|
+
responseStatus: data?.statusCode || "",
|
|
205
|
+
requestBody: normalizePayload(data?.requestBody || {}),
|
|
206
|
+
responseBody: normalizePayload(data?.responseBody || {}),
|
|
198
207
|
};
|
|
199
208
|
|
|
200
209
|
const redactedData = redactInformation(logData, {
|
|
@@ -208,10 +217,7 @@ function createLogger(config) {
|
|
|
208
217
|
const traceContext = getTraceContext();
|
|
209
218
|
const errorResponse = data?.error?.response;
|
|
210
219
|
const tokenDetails = decodeTokenDetails(
|
|
211
|
-
data?.headers ||
|
|
212
|
-
errorResponse?.config?.headers ||
|
|
213
|
-
req?.headers ||
|
|
214
|
-
{}
|
|
220
|
+
data?.headers || errorResponse?.config?.headers || req?.headers || {},
|
|
215
221
|
);
|
|
216
222
|
|
|
217
223
|
const logData = {
|
|
@@ -220,24 +226,24 @@ function createLogger(config) {
|
|
|
220
226
|
parentSpan: traceContext.parent_span_id,
|
|
221
227
|
thread: "",
|
|
222
228
|
class: "",
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
229
|
+
message: data?.error?.message || "",
|
|
230
|
+
step: data?.step || "",
|
|
231
|
+
method: errorResponse?.config?.method?.toUpperCase() || "",
|
|
232
|
+
parameters: normalizePayload(data?.parameters || ""),
|
|
233
|
+
path: errorResponse?.config?.url || req?.originalUrl || "",
|
|
234
|
+
responseStatus: errorResponse?.status || HTTP_STATUS_CODES.BAD_REQUEST,
|
|
235
|
+
headers: normalizePayload(
|
|
236
|
+
errorResponse?.config?.headers || req?.headers || {},
|
|
237
|
+
),
|
|
238
|
+
requestBody: normalizePayload(
|
|
239
|
+
errorResponse?.config?.data ||
|
|
234
240
|
errorResponse?.config?.body ||
|
|
235
241
|
req?.body ||
|
|
236
242
|
{},
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
243
|
+
),
|
|
244
|
+
responseBody: normalizePayload(errorResponse?.data || {}),
|
|
245
|
+
stack: data?.error?.stack || "",
|
|
246
|
+
tokenDetails,
|
|
241
247
|
};
|
|
242
248
|
|
|
243
249
|
const redactedData = redactInformation(logData, {
|
|
@@ -250,14 +256,20 @@ function createLogger(config) {
|
|
|
250
256
|
logDebug: (message, data = {}) => {
|
|
251
257
|
const traceContext = getTraceContext();
|
|
252
258
|
|
|
253
|
-
|
|
259
|
+
const logData = {
|
|
254
260
|
trace: traceContext.trace_id,
|
|
255
261
|
span: traceContext.span_id,
|
|
256
262
|
parentSpan: traceContext.parent_span_id,
|
|
257
|
-
|
|
263
|
+
message:
|
|
258
264
|
typeof message === "string" ? message : safeJsonStringify(message),
|
|
259
265
|
...data,
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const redactedData = redactInformation(logData, {
|
|
269
|
+
sensitiveKeys: globalConfig?.SENSITIVE_KEYS,
|
|
260
270
|
});
|
|
271
|
+
|
|
272
|
+
emitLog("debug", redactedData);
|
|
261
273
|
},
|
|
262
274
|
|
|
263
275
|
flush: async () => {
|
|
@@ -278,4 +290,4 @@ module.exports = {
|
|
|
278
290
|
createLogger,
|
|
279
291
|
setContextProvider,
|
|
280
292
|
logger: loggerInstance,
|
|
281
|
-
};
|
|
293
|
+
};
|
|
@@ -1,61 +1,102 @@
|
|
|
1
1
|
/** This file will be used to redact the sensitive informations to be logged */
|
|
2
2
|
|
|
3
|
-
const traverse =
|
|
4
|
-
const { klona }
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
const traverse = require("traverse");
|
|
4
|
+
const { klona } = require("klona/full");
|
|
5
|
+
|
|
6
|
+
const defaultSensitiveKeys = [
|
|
7
|
+
/cookie/i,
|
|
8
|
+
/passw(or)?d/i,
|
|
9
|
+
/^pw$/i,
|
|
10
|
+
/^pass$/i,
|
|
11
|
+
/secret/i,
|
|
12
|
+
/api[-._]?key/i,
|
|
13
|
+
/token/i,
|
|
14
|
+
/authorization/i,
|
|
15
|
+
/otp/i,
|
|
16
|
+
/^pin$/i,
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function buildSensitiveRegexList(sensitiveKeys) {
|
|
20
|
+
try {
|
|
21
|
+
if (!sensitiveKeys) return defaultSensitiveKeys;
|
|
22
|
+
|
|
23
|
+
const parsed =
|
|
24
|
+
typeof sensitiveKeys === "string" ? JSON.parse(sensitiveKeys) : sensitiveKeys;
|
|
25
|
+
|
|
26
|
+
if (!Array.isArray(parsed) || !parsed.length) {
|
|
27
|
+
return defaultSensitiveKeys;
|
|
13
28
|
}
|
|
14
|
-
return SENSITIVE_KEYS.some(regex => regex.test(keyStr));
|
|
15
|
-
}
|
|
16
29
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
30
|
+
return parsed.map(key => {
|
|
31
|
+
if (key instanceof RegExp) return key;
|
|
32
|
+
|
|
33
|
+
const parts =
|
|
34
|
+
typeof key === "string" && key.match(/^\/(.*?)\/([gimsuy]*)$/);
|
|
35
|
+
|
|
36
|
+
return parts ? new RegExp(parts[1], parts[2]) : new RegExp(key, "i");
|
|
24
37
|
});
|
|
38
|
+
} catch (error) {
|
|
39
|
+
return defaultSensitiveKeys;
|
|
40
|
+
}
|
|
25
41
|
}
|
|
26
42
|
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
parsedMessage = JSON.parse(this?.node);
|
|
36
|
-
redactNestedFields(parsedMessage);
|
|
37
|
-
} catch (error) {
|
|
38
|
-
parsedMessage = {};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
this.update(JSON.stringify(parsedMessage)); // Update with the redacted string
|
|
42
|
-
}
|
|
43
|
-
});
|
|
43
|
+
function isSensitiveKey(keyStr, sensitiveRegexList) {
|
|
44
|
+
if (!keyStr) return false;
|
|
45
|
+
|
|
46
|
+
if (/^tokenDetails$/i.test(keyStr)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return sensitiveRegexList.some(regex => regex.test(String(keyStr)));
|
|
44
51
|
}
|
|
45
52
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const parts = key.match(/^\/(.*?)\/([gimsuy]*)$/);
|
|
49
|
-
return parts ? new RegExp(parts[1], parts[2]) : new RegExp(key); // Handle conversion
|
|
50
|
-
});
|
|
51
|
-
SENSITIVE_KEYS = regexArray || defautSensitiveKeys;
|
|
52
|
-
const copy = klona(obj); // Making a deep copy to prevent side effects
|
|
53
|
-
redactObject(copy);
|
|
53
|
+
function redactNestedFields(obj, sensitiveRegexList) {
|
|
54
|
+
if (!obj || typeof obj !== "object") return;
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
Object.entries(obj).forEach(([key, value]) => {
|
|
57
|
+
if (isSensitiveKey(key, sensitiveRegexList)) {
|
|
58
|
+
obj[key] = "[REDACTED]";
|
|
59
|
+
} else if (value && typeof value === "object") {
|
|
60
|
+
redactNestedFields(value, sensitiveRegexList);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function redactObject(obj, sensitiveRegexList) {
|
|
66
|
+
if (!obj || typeof obj !== "object") return;
|
|
57
67
|
|
|
58
|
-
|
|
68
|
+
traverse(obj).forEach(function redactor() {
|
|
69
|
+
if (isSensitiveKey(this.key, sensitiveRegexList)) {
|
|
70
|
+
this.update("[REDACTED]");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (this.key === "message" && typeof this.node === "string") {
|
|
75
|
+
try {
|
|
76
|
+
const parsedMessage = JSON.parse(this.node);
|
|
77
|
+
if (parsedMessage && typeof parsedMessage === "object") {
|
|
78
|
+
redactNestedFields(parsedMessage, sensitiveRegexList);
|
|
79
|
+
this.update(JSON.stringify(parsedMessage));
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
// keep original plain-text message unchanged
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
59
86
|
}
|
|
60
87
|
|
|
61
|
-
|
|
88
|
+
const redactInformation = (obj, { sensitiveKeys } = {}) => {
|
|
89
|
+
const sensitiveRegexList = buildSensitiveRegexList(sensitiveKeys);
|
|
90
|
+
const copy = klona(obj);
|
|
91
|
+
|
|
92
|
+
redactObject(copy, sensitiveRegexList);
|
|
93
|
+
|
|
94
|
+
const splat = copy?.[Symbol.for("splat")];
|
|
95
|
+
if (splat) {
|
|
96
|
+
redactObject(splat, sensitiveRegexList);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return copy;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
module.exports = { redactInformation };
|