UNPKG

3.3 kBJavaScriptView Raw
1const defaults = {
2 logger: console.log,
3 awsContext: false,
4 omitPaths: [],
5 mask: undefined,
6 replacer: undefined
7};
8const inputOutputLoggerMiddleware = (opts = {})=>{
9 const { logger, awsContext, omitPaths, mask, replacer } = {
10 ...defaults,
11 ...opts
12 };
13 if (typeof logger !== 'function') {
14 throw new Error('logger must be a function', {
15 cause: {
16 package: '@middy/input-output-logger'
17 }
18 });
19 }
20 const omitPathTree = buildPathTree(omitPaths);
21 const omitAndLog = (param, request)=>{
22 const message = {
23 [param]: request[param]
24 };
25 if (awsContext) {
26 message.context = pick(request.context, awsContextKeys);
27 }
28 let cloneMessage = message;
29 if (omitPaths.length) {
30 cloneMessage = structuredClone(message, replacer);
31 omit(cloneMessage, {
32 [param]: omitPathTree[param]
33 });
34 }
35 logger(cloneMessage);
36 };
37 const omit = (obj, pathTree = {})=>{
38 if (Array.isArray(obj) && pathTree['[]']) {
39 for(let i = 0, l = obj.length; i < l; i++){
40 omit(obj[i], pathTree['[]']);
41 }
42 } else if (isObject(obj)) {
43 for(const key in pathTree){
44 if (pathTree[key] === true) {
45 if (mask) {
46 obj[key] = mask;
47 } else {
48 delete obj[key];
49 }
50 } else {
51 omit(obj[key], pathTree[key]);
52 }
53 }
54 }
55 };
56 const inputOutputLoggerMiddlewareBefore = async (request)=>omitAndLog('event', request);
57 const inputOutputLoggerMiddlewareAfter = async (request)=>omitAndLog('response', request);
58 const inputOutputLoggerMiddlewareOnError = async (request)=>{
59 if (request.response === undefined) return;
60 omitAndLog('response', request);
61 };
62 return {
63 before: inputOutputLoggerMiddlewareBefore,
64 after: inputOutputLoggerMiddlewareAfter,
65 onError: inputOutputLoggerMiddlewareOnError
66 };
67};
68const awsContextKeys = [
69 'functionName',
70 'functionVersion',
71 'invokedFunctionArn',
72 'memoryLimitInMB',
73 'awsRequestId',
74 'logGroupName',
75 'logStreamName',
76 'identity',
77 'clientContext',
78 'callbackWaitsForEmptyEventLoop'
79];
80const pick = (originalObject = {}, keysToPick = [])=>{
81 const newObject = {};
82 for (const path of keysToPick){
83 if (originalObject[path] !== undefined) {
84 newObject[path] = originalObject[path];
85 }
86 }
87 return newObject;
88};
89const buildPathTree = (paths)=>{
90 const tree = {};
91 for (let path of paths.sort().reverse()){
92 if (!Array.isArray(path)) path = path.split('.');
93 if (path.includes('__proto__')) continue;
94 path.slice(0).reduce((a, b, idx)=>{
95 if (idx < path.length - 1) {
96 a[b] ??= {};
97 return a[b];
98 }
99 a[b] = true;
100 return true;
101 }, tree);
102 }
103 return tree;
104};
105const isObject = (value)=>value && typeof value === 'object' && value.constructor === Object;
106export default inputOutputLoggerMiddleware;
107