sasai-common-utils 1.0.51 → 1.0.52
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;
|
|
@@ -159,7 +164,7 @@ function emitLog(level, data) {
|
|
|
159
164
|
trace_id: trace || undefined,
|
|
160
165
|
span_id: span || undefined,
|
|
161
166
|
parent_span_id: parentSpan || undefined,
|
|
162
|
-
message: cleanString(message),
|
|
167
|
+
message: cleanString(message || ""),
|
|
163
168
|
...rest,
|
|
164
169
|
});
|
|
165
170
|
}
|
|
@@ -181,20 +186,18 @@ function createLogger(config) {
|
|
|
181
186
|
parentSpan: traceContext.parent_span_id,
|
|
182
187
|
thread: "",
|
|
183
188
|
class: "",
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
responseBody: data?.responseBody || {},
|
|
197
|
-
}),
|
|
189
|
+
message,
|
|
190
|
+
step: data?.step || "",
|
|
191
|
+
method: data?.method?.toUpperCase() || "",
|
|
192
|
+
parameters: normalizePayload(data?.parameters || ""),
|
|
193
|
+
path: data?.url || "",
|
|
194
|
+
headers: normalizePayload(data?.headers || {}),
|
|
195
|
+
responseHeaders: normalizePayload(data?.responseHeaders || {}),
|
|
196
|
+
api: data?.api || "",
|
|
197
|
+
tokenDetails,
|
|
198
|
+
responseStatus: data?.statusCode || "",
|
|
199
|
+
requestBody: normalizePayload(data?.requestBody || {}),
|
|
200
|
+
responseBody: normalizePayload(data?.responseBody || {}),
|
|
198
201
|
};
|
|
199
202
|
|
|
200
203
|
const redactedData = redactInformation(logData, {
|
|
@@ -208,10 +211,7 @@ function createLogger(config) {
|
|
|
208
211
|
const traceContext = getTraceContext();
|
|
209
212
|
const errorResponse = data?.error?.response;
|
|
210
213
|
const tokenDetails = decodeTokenDetails(
|
|
211
|
-
data?.headers ||
|
|
212
|
-
errorResponse?.config?.headers ||
|
|
213
|
-
req?.headers ||
|
|
214
|
-
{}
|
|
214
|
+
data?.headers || errorResponse?.config?.headers || req?.headers || {},
|
|
215
215
|
);
|
|
216
216
|
|
|
217
217
|
const logData = {
|
|
@@ -220,24 +220,24 @@ function createLogger(config) {
|
|
|
220
220
|
parentSpan: traceContext.parent_span_id,
|
|
221
221
|
thread: "",
|
|
222
222
|
class: "",
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
223
|
+
message: data?.error?.message || "",
|
|
224
|
+
step: data?.step || "",
|
|
225
|
+
method: errorResponse?.config?.method?.toUpperCase() || "",
|
|
226
|
+
parameters: normalizePayload(data?.parameters || ""),
|
|
227
|
+
path: errorResponse?.config?.url || req?.originalUrl || "",
|
|
228
|
+
responseStatus: errorResponse?.status || HTTP_STATUS_CODES.BAD_REQUEST,
|
|
229
|
+
headers: normalizePayload(
|
|
230
|
+
errorResponse?.config?.headers || req?.headers || {},
|
|
231
|
+
),
|
|
232
|
+
requestBody: normalizePayload(
|
|
233
|
+
errorResponse?.config?.data ||
|
|
234
234
|
errorResponse?.config?.body ||
|
|
235
235
|
req?.body ||
|
|
236
236
|
{},
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
),
|
|
238
|
+
responseBody: normalizePayload(errorResponse?.data || {}),
|
|
239
|
+
stack: data?.error?.stack || "",
|
|
240
|
+
tokenDetails,
|
|
241
241
|
};
|
|
242
242
|
|
|
243
243
|
const redactedData = redactInformation(logData, {
|
|
@@ -250,14 +250,20 @@ function createLogger(config) {
|
|
|
250
250
|
logDebug: (message, data = {}) => {
|
|
251
251
|
const traceContext = getTraceContext();
|
|
252
252
|
|
|
253
|
-
|
|
253
|
+
const logData = {
|
|
254
254
|
trace: traceContext.trace_id,
|
|
255
255
|
span: traceContext.span_id,
|
|
256
256
|
parentSpan: traceContext.parent_span_id,
|
|
257
|
-
|
|
257
|
+
message:
|
|
258
258
|
typeof message === "string" ? message : safeJsonStringify(message),
|
|
259
259
|
...data,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const redactedData = redactInformation(logData, {
|
|
263
|
+
sensitiveKeys: globalConfig?.SENSITIVE_KEYS,
|
|
260
264
|
});
|
|
265
|
+
|
|
266
|
+
emitLog("debug", redactedData);
|
|
261
267
|
},
|
|
262
268
|
|
|
263
269
|
flush: async () => {
|
|
@@ -278,4 +284,4 @@ module.exports = {
|
|
|
278
284
|
createLogger,
|
|
279
285
|
setContextProvider,
|
|
280
286
|
logger: loggerInstance,
|
|
281
|
-
};
|
|
287
|
+
};
|
|
@@ -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 };
|